Skip to content

Commit c883b8b

Browse files
Web 0.13.4: Haken „Pseudocode“ in der Werkzeugzeile, Simulation von Pseudocode (#200)
* Web 0.13.2: Haken „Pseudocode“ in der Werkzeugzeile, Simulation von Pseudocode Der Haken steht jetzt links vom ⇄. Bei Pseudocode führt die Simulation die Kästen nicht als Java aus, sondern zeigt sie nacheinander und fragt bei Bedingungen, Schleifen und Fallauswahl nach der Antwort. Programmieren zeigt keine Java-Fehler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ugz28VNNwmqwKtMcjdywfT * Lektion 1: Spaghetticode schon bei „keine Sprünge kreuz und quer“ Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ugz28VNNwmqwKtMcjdywfT --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent ff9d8c3 commit c883b8b

59 files changed

Lines changed: 472 additions & 299 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The format is loosely inspired by Keep a Changelog.
88

99
### Added
1010

11+
- **Web 0.13.4, Pseudocode switch in the toolbar and simulation of pseudocode:** the checkbox „Pseudocode“ now sits in the toolbar left of ⇄ (before it was only in the code window). For a diagram marked as pseudocode (e.g. „Tee kochen“ in Lektion 1) the simulation no longer runs the boxes as Java: it only highlights them one after another, and for a condition („Regnet es?“), a loop or a Fallauswahl it asks for the answer (true/false or ja/nein, case name). Programmieren shows no Java errors for pseudocode. Lektion 1 now mentions spaghetti code right where it says a Struktogramm has no jumps.
1112
- **Web 0.13.3, Struktogramm and Elemente in the code block colours:** the Struktogramm uses the same soft colours as the blocks in Programmieren: statements yellow like the method, loops (for, while, do-until, endless) pink, Verzweigung and Fallauswahl blue; elements inside a loop or branch take its colour, so code and diagram side by side show the matching blocks at a glance. The boxes in „Elemente“ get the same colours. The setting is now called „Blöcke farbig“ and switches code, diagram and Elemente together; a colour stored in the file still wins, and exports stay white. Lesson 1 uses the same colours (statements yellow, branch blue, loop pink; the sequence itself has no colour, it is the row of statements).
1213
- **Web 0.13.1, divider between Struktogramm and Programmieren always draggable:** with four open columns (lessons, Struktogramm, Programmieren, „mBot2 live“) the diagram sat at its minimum width and the divider barely moved. Dragging it now first uses the space of the two columns next to it, then takes width from „mBot2 live“, then from the lessons and files columns (down to their minimum widths). Works in both orders after ⇄; all widths are remembered.
1314
- **Web 0.13.0, VisuBot simulation in the „mBot2 live“ window:** in the VisuBot world „Simulation starten“ no longer shows a second 3D world in the simulation column; the mBot2 of the simulation drives in the „mBot2 live“ window instead (title „🤖 Simulation“, world buttons and distance/line values there, its own ▶ rests until the simulation ends). The window opens by itself for the simulation and closes again afterwards if it was closed before; if it is closed during the simulation, the 3D world goes back to the simulation column. Controls, variables, console, sensor sliders and the real mBot2 stay in the simulation column.

‎web/lessons/de/01-was-ist-ein-struktogramm.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Die Darstellung stammt von **Isaac Nassi und Ben Shneiderman** (1973). Deshalb h
2424
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).
2525
:::
2626

27-
> Ein Struktogramm hat immer genau einen Anfang (oben) und ein Ende (unten). Es gibt keine Pfeile und keine Sprünge kreuz und quer.
27+
> 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 Schritt 8.
2828
2929
::: angeberwissen
3030
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.

‎web/lessons/en/01-was-ist-ein-struktogramm.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ This way of drawing was invented by **Isaac Nassi and Ben Shneiderman** (1973).
2424
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).
2525
:::
2626

27-
> 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.
27+
> 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 step 8.
2828
2929
::: angeberwissen
3030
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.

‎web/lessons/pt/01-was-ist-ein-struktogramm.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Esta forma de desenhar foi inventada por **Isaac Nassi e Ben Shneiderman** (1973
2424
À 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).
2525
:::
2626

27-
> 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.
27+
> 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 no passo 8.
2828
2929
::: angeberwissen
3030
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.

‎web/package-lock.json‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎web/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "visustruct-web",
33
"private": true,
4-
"version": "0.13.3",
4+
"version": "0.13.4",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

‎web/src/core/simulation.ts‎

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export interface InputRequest {
6565
prompt: string
6666
/** Wie in Java hinter `System.out.print("Note: ")`: Die Eingabe steht in derselben Zeile wie diese Ausgabe. */
6767
continuesLine?: boolean
68+
/** Pseudocode: Die Frage gehört zu einer Bedingung, die Antwort (true/false) entscheidet den Weg. */
69+
decision?: boolean
6870
}
6971

7072
/** Meldungen der Simulation, unabhängig von der Sprache. */
@@ -961,12 +963,17 @@ export class SimulationEngine {
961963
private peeking = false
962964
/** Messwerte des echten mBot2; fehlt ein Wert, gilt der simulierte aus `MBOT_SENSORS`. */
963965
robotSensors: MBotSensorValues = {}
966+
/** Pseudocode: Kästen werden nur der Reihe nach gezeigt, Bedingungen beantwortet man selbst. */
967+
private pseudocode: boolean
968+
/** Pseudocode: Antwort auf die gerade gestellte Frage (Bedingung: true/false, Fallauswahl: Name des Falls). */
969+
private decided?: boolean | string
964970

965971
constructor(
966972
document: VSDocument,
967973
private resolveSubprogram: SubprogramResolver = () => undefined,
968974
) {
969975
this.steps = buildSteps(document.elements)
976+
this.pseudocode = !!document.pseudocode
970977
}
971978

972979
/** Im Unterprogramm bleibt der Aufruf-Kasten im Hauptprogramm markiert. */
@@ -985,7 +992,11 @@ export class SimulationEngine {
985992
}
986993

987994
reset(document?: VSDocument): void {
988-
if (document) this.steps = buildSteps(document.elements)
995+
if (document) {
996+
this.steps = buildSteps(document.elements)
997+
this.pseudocode = !!document.pseudocode
998+
}
999+
this.decided = undefined
9891000
this.state = freshState()
9901001
this.afterOutput = false
9911002
this.nextFrame = 1
@@ -1003,6 +1014,11 @@ export class SimulationEngine {
10031014
try {
10041015
switch (step.kind) {
10051016
case 'statement':
1017+
// Pseudocode ist kein Programm: Der Kasten wird nur gezeigt, nicht ausgeführt.
1018+
if (this.pseudocode) {
1019+
state.lastTrace = step.text
1020+
break
1021+
}
10061022
if (/^return\b/u.test(step.text)) return this.executeReturn(step)
10071023
this.executeStatement(step.text)
10081024
break
@@ -1034,6 +1050,7 @@ export class SimulationEngine {
10341050
provideInput(rawValue: string): void {
10351051
const request = this.state.inputRequest
10361052
if (!request) return
1053+
if (request.decision) return this.provideDecision(rawValue)
10371054
try {
10381055
const value = parseInputValue(rawValue, request.valueType)
10391056
this.assignValue(value, request.variableName)
@@ -1048,6 +1065,42 @@ export class SimulationEngine {
10481065
this.state.notice = this.finishedIfAtEnd()
10491066
}
10501067

1068+
/** Pseudocode: Antwort auf die Frage zur Bedingung, dann gleich den Weg nehmen (die Bedingung ist der Schritt). */
1069+
private provideDecision(rawValue: string): void {
1070+
if (this.state.inputRequest?.valueType === 'string') {
1071+
this.state.inputRequest = undefined
1072+
this.decided = rawValue.trim()
1073+
return this.step()
1074+
}
1075+
const answer = rawValue.trim().toLowerCase()
1076+
const yes = ['true', 'wahr', 'ja', 'yes', 'sim', 'verdadeiro'].includes(answer)
1077+
const no = ['false', 'falsch', 'nein', 'no', 'não', 'nao', 'falso'].includes(answer)
1078+
if (!yes && !no) {
1079+
this.state.inputErrorNotice = { kind: 'booleanExpected' }
1080+
return
1081+
}
1082+
this.state.inputRequest = undefined
1083+
this.state.inputErrorNotice = undefined
1084+
this.decided = yes
1085+
this.step()
1086+
}
1087+
1088+
/**
1089+
* Wert einer Bedingung. Bei Pseudocode (z. B. „Regnet es?“) fragt die Simulation: `undefined`, bis die Antwort da ist.
1090+
*/
1091+
private condition(condition: string): boolean | undefined {
1092+
if (!this.pseudocode) return this.evaluateBool(condition)
1093+
const answer = this.decided
1094+
if (typeof answer === 'boolean') {
1095+
this.decided = undefined
1096+
return answer
1097+
}
1098+
this.state.inputRequest = { variableName: '', valueType: 'boolean', prompt: `${condition} (true/false)`, decision: true }
1099+
this.state.inputErrorNotice = undefined
1100+
this.state.notice = undefined
1101+
return undefined
1102+
}
1103+
10511104
private finishedIfAtEnd(): SimNotice | undefined {
10521105
return this.state.stepIndex >= this.steps.length ? { kind: 'finished' } : undefined
10531106
}
@@ -1065,7 +1118,8 @@ export class SimulationEngine {
10651118
const cases = step.cases
10661119
if (!cases || cases.length < 2) throw unsupported(step.text)
10671120
const condition = stripConditionKeyword(step.text)
1068-
const result = this.evaluateBool(condition)
1121+
const result = this.condition(condition)
1122+
if (result === undefined) return
10691123
const selected = result ? 0 : 1
10701124
const branchSteps = buildSteps(cases[selected].elements, [...step.path, selected], contextOf(step))
10711125
this.splice(branchSteps)
@@ -1077,15 +1131,29 @@ export class SimulationEngine {
10771131
const cases = step.cases
10781132
if (!cases || cases.length === 0) throw unsupported(step.text)
10791133
const selector = stripConditionKeyword(step.text)
1080-
const value = this.evaluateSwitchSelector(selector)
1081-
const selected = matchingCaseIndex(value, cases) ?? cases.length - 1
1134+
let selected: number
1135+
if (this.pseudocode) {
1136+
// Pseudocode: Man wählt den Fall selbst; unbekannte Antwort = letzter Fall (sonst).
1137+
const answer = this.decided
1138+
if (typeof answer !== 'string') {
1139+
const names = cases.map((c) => c.name).join(' / ')
1140+
this.state.inputRequest = { variableName: '', valueType: 'string', prompt: `${selector} (${names})`, decision: true }
1141+
this.state.notice = undefined
1142+
return
1143+
}
1144+
this.decided = undefined
1145+
const index = cases.findIndex((c) => c.name.trim().toLowerCase() === answer.toLowerCase())
1146+
selected = index >= 0 ? index : cases.length - 1
1147+
} else selected = matchingCaseIndex(this.evaluateSwitchSelector(selector), cases) ?? cases.length - 1
10821148
const caseSteps = buildSteps(cases[selected].elements, [...step.path, selected], contextOf(step))
10831149
this.splice(caseSteps)
10841150
this.state.lastTrace = `${selector} -> ${cases[selected].name}`
10851151
this.state.notice = this.noticeAfterEntering(caseSteps)
10861152
}
10871153

10881154
private executeForLoopStart(step: SimStep): void {
1155+
// Pseudocode wie „für jeden Gast“: eine Frage wie bei der kopfgesteuerten Schleife.
1156+
if (this.pseudocode) return this.splice([{ ...step, kind: 'whileLoop' }])
10891157
const parts = normalizedForParts(step.forParts ?? [step.text])
10901158
if (parts[0] !== '') this.executeStatement(parts[0])
10911159
this.splice([{ ...contextOf(step), path: step.path, text: parts[1], kind: 'forLoopCondition', loopBody: step.loopBody, forParts: parts }])
@@ -1094,7 +1162,8 @@ export class SimulationEngine {
10941162
private executeForLoopCondition(step: SimStep): void {
10951163
const parts = normalizedForParts(step.forParts ?? [step.text])
10961164
const condition = parts[1] === '' ? 'true' : parts[1]
1097-
const shouldEnter = this.evaluateBool(condition)
1165+
const shouldEnter = this.condition(condition)
1166+
if (shouldEnter === undefined) return
10981167
this.state.lastTrace = `${condition} -> ${shouldEnter}`
10991168
if (shouldEnter) {
11001169
const bodySteps = buildSteps(step.loopBody ?? [], [...step.path, 0], contextOf(step))
@@ -1117,7 +1186,8 @@ export class SimulationEngine {
11171186

11181187
private executeWhileLoop(step: SimStep): void {
11191188
const condition = stripConditionKeyword(step.text)
1120-
const shouldEnter = this.evaluateBool(condition)
1189+
const shouldEnter = this.condition(condition)
1190+
if (shouldEnter === undefined) return
11211191
this.state.lastTrace = `${condition} -> ${shouldEnter}`
11221192
if (shouldEnter) {
11231193
const bodySteps = buildSteps(step.loopBody ?? [], [...step.path, 0], contextOf(step))
@@ -1137,7 +1207,8 @@ export class SimulationEngine {
11371207

11381208
private executeDoUntilCondition(step: SimStep): void {
11391209
const condition = stripConditionKeyword(step.text)
1140-
const shouldRepeat = this.evaluateBool(condition)
1210+
const shouldRepeat = this.condition(condition)
1211+
if (shouldRepeat === undefined) return
11411212
this.state.lastTrace = `${condition} -> ${shouldRepeat}`
11421213
if (shouldRepeat) {
11431214
const bodySteps = buildSteps(step.loopBody ?? [], [...step.path, 0], contextOf(step))

‎web/src/i18n/ar.strings‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,4 @@
176176
"web_palette_show" = "إظهار العناصر";
177177
"web_code_scopes" = "كتل الشيفرة ملوّنة";
178178
"web_code_scopes_hint" = "الصنف والدالة والحلقة والتفرّع في مربعات خاصة";
179+
"web_pseudocode" = "شبه كود";

‎web/src/i18n/da.strings‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,4 @@
176176
"web_palette_show" = "Vis elementerne";
177177
"web_code_scopes" = "Farvede kodeblokke";
178178
"web_code_scopes_hint" = "Klasse, metode, løkke, forgrening i egne felter";
179+
"web_pseudocode" = "Pseudokode";

‎web/src/i18n/de.strings‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,3 +665,4 @@
665665
"web_visuobject_method_ph" = "+ bellen(): void";
666666
"web_code_scopes" = "Blöcke farbig";
667667
"web_code_scopes_hint" = "Code und Struktogramm: Schleife rosa, Verzweigung blau, Methode gelb, Klasse grün";
668+
"web_pseudocode" = "Pseudocode";

0 commit comments

Comments
 (0)