Skip to content

Commit 0df40f2

Browse files
author
rmen527
committed
Add better output messages
1 parent f3c7c8d commit 0df40f2

2 files changed

Lines changed: 37 additions & 29 deletions

File tree

src/components/Codebox.tsx

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from "react";
22

33
export type ExpectedStep =
4-
| { kind: "assign"; var: string; val: number }
4+
| { kind: "assign"; var: string; val: number; srcToken?: string }
55
| { kind: "print"; val: number };
66

77
interface CodeboxProps {
@@ -19,7 +19,7 @@ function approxEqual(a: number, b: number): boolean {
1919
}
2020

2121
type AlienStep =
22-
| { kind: "assign"; var: string; val: number; lineNum: number }
22+
| { kind: "assign"; var: string; val: number; lineNum: number; srcToken?: string }
2323
| { kind: "print"; val: number; lineNum: number };
2424

2525
function checkTrace(
@@ -38,6 +38,8 @@ function checkTrace(
3838
if (a.kind === "assign" && e.kind === "assign") {
3939
if (a.var !== e.var || !approxEqual(a.val, e.val))
4040
return `Alien Error [Line ${a.lineNum}]: Translation incorrect`;
41+
if (e.srcToken !== undefined && a.srcToken !== e.srcToken)
42+
return `Alien Error [Line ${a.lineNum}]: Translation incorrect`;
4143
}
4244
if (a.kind === "print" && e.kind === "print") {
4345
if (!approxEqual(a.val, e.val))
@@ -96,44 +98,40 @@ function runLevel1(
9698
}
9799

98100
vars[varName] = result;
99-
alienTrace.push({ kind: "assign", var: varName, val: result, lineNum });
101+
alienTrace.push({ kind: "assign", var: varName, val: result, lineNum, srcToken: valToken });
100102
}
101103

102-
if (vars["z"] === undefined)
103-
return { output: "No output", error: true };
104-
105-
const lastLine = lines[lines.length - 1].lineNum;
106-
alienTrace.push({ kind: "print", val: vars["z"], lineNum: lastLine });
107-
108104
const traceErr = checkTrace(alienTrace, expectedTrace);
109105
if (traceErr) return { output: traceErr, error: true };
110106

111-
return { output: String(vars["z"]), error: false };
107+
return { output: "Correct! Well done!", error: false };
112108
}
113109

114110
// ---- Level 2: :) assignment, :( print ----
115111
// Expressions: only literal 1, vars x/y/z, operators +-*^, parens.
116-
function validateExprL2(expr: string): boolean {
112+
// Returns null if valid, or the offending token string if invalid.
113+
function validateExprL2(expr: string): string | null {
117114
const s = expr.replace(/\s/g, "");
118-
if (s.length === 0 || s.includes("**")) return false;
115+
if (s.length === 0) return expr.trim() || "empty";
116+
if (s.includes("**")) return "**";
119117
let i = 0;
120118
while (i < s.length) {
121119
const ch = s[i];
122120
if ("xyz".includes(ch)) {
123-
if (i + 1 < s.length && /[a-zA-Z]/.test(s[i + 1])) return false;
121+
if (i + 1 < s.length && /[a-zA-Z]/.test(s[i + 1])) return ch + s[i + 1];
124122
i++;
125123
} else if (ch === "1") {
126-
if (i + 1 < s.length && /[0-9]/.test(s[i + 1])) return false;
124+
if (i + 1 < s.length && /[0-9]/.test(s[i + 1])) return ch + s[i + 1];
127125
i++;
128126
} else if (/[0-9]/.test(ch)) {
129-
return false;
127+
return ch;
130128
} else if ("+-*^()".includes(ch)) {
131129
i++;
132130
} else {
133-
return false;
131+
return ch;
134132
}
135133
}
136-
return true;
134+
return null;
137135
}
138136

139137
function runLevel2(
@@ -170,8 +168,9 @@ function runLevel2(
170168
if (assignMatch) {
171169
const varName = assignMatch[1];
172170
const expr = assignMatch[2].trim();
173-
if (!validateExprL2(expr))
174-
return { output: `Alien Error [Line ${lineNum}]: Syntax Error`, error: true };
171+
const badToken = validateExprL2(expr);
172+
if (badToken !== null)
173+
return { output: `Alien Error [Line ${lineNum}]: Syntax Error ("${badToken}" is not defined)`, error: true };
175174

176175
const exprClean = expr.replace(/\s/g, "");
177176
for (const v of ["x", "y", "z"]) {
@@ -194,7 +193,13 @@ function runLevel2(
194193
continue;
195194
}
196195

197-
return { output: `Alien Error [Line ${lineNum}]: Syntax Error`, error: true };
196+
const badCh = line.split("").find(ch => !/[\sxyz:()+\-*^1]/.test(ch));
197+
return {
198+
output: badCh
199+
? `Alien Error [Line ${lineNum}]: Syntax Error ("${badCh}" is not defined)`
200+
: `Alien Error [Line ${lineNum}]: Syntax Error`,
201+
error: true,
202+
};
198203
}
199204

200205
if (outputLines.length === 0)
@@ -203,7 +208,7 @@ function runLevel2(
203208
const traceErr = checkTrace(alienTrace, expectedTrace);
204209
if (traceErr) return { output: traceErr, error: true };
205210

206-
return { output: outputLines.join("\n"), error: false };
211+
return { output: outputLines.join("\n") + "\n\nCorrect!", error: false };
207212
}
208213

209214
// ---- Level 3: array init + copy ops + output ----
@@ -264,7 +269,7 @@ function runLevel3(
264269
if (expectedOutputStr && outputStr !== expectedOutputStr)
265270
return { output: "Alien Error: Translation incorrect", error: true };
266271

267-
return { output: outputStr, error: false };
272+
return { output: outputStr + "\n\nCorrect!", error: false };
268273
}
269274

270275
// ---- component ----
@@ -298,6 +303,7 @@ export default function Codebox(props: CodeboxProps) {
298303
value={code}
299304
onChange={(e) => setCode(e.target.value)}
300305
placeholder="Write alien code here!"
306+
spellCheck={false}
301307
className="p-6 text-2xl mx-4 rounded-lg resize-none flex-1 min-h-0"
302308
/>
303309

src/pages/home/Home.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const LEVEL_CONFIGS: Record<number, {
1313
1: {
1414
pythonCode: `x = 3\ny = 3\nx += 2\ny -= x`,
1515
alienCode: `HI x, 3\nHI y, 3\nYAY x, 2\nNO y, x`,
16-
task: `Translate this Python code into Alien code:\nx = 2\ny = 1\nz = x\nz -= x\nz += y`,
16+
task: `Translate this Python code into Alien code:\nx = 2\ny = x\ny += x`,
1717
},
1818
2: {
1919
pythonCode: `x = 5\ny = x - 3\nz = x ** 2\nprint(z)`,
@@ -53,24 +53,26 @@ function evalPythonTaskL1(taskText: string): ExpectedStep[] {
5353
const subEq = line.match(/^([xyz])\s*-=\s*(.+)$/);
5454
const assign = line.match(/^([xyz])\s*=\s*(.+)$/);
5555

56+
const simpleToken = (expr: string) => {
57+
const t = expr.trim();
58+
return /^[xyz]$/.test(t) || /^[0-9]+$/.test(t) ? t : undefined;
59+
};
60+
5661
if (addEq) {
5762
const [, v, expr] = addEq;
5863
vars[v] = (vars[v] ?? 0) + evalExpr(expr);
59-
trace.push({ kind: "assign", var: v, val: vars[v] });
64+
trace.push({ kind: "assign", var: v, val: vars[v], srcToken: simpleToken(expr) });
6065
} else if (subEq) {
6166
const [, v, expr] = subEq;
6267
vars[v] = (vars[v] ?? 0) - evalExpr(expr);
63-
trace.push({ kind: "assign", var: v, val: vars[v] });
68+
trace.push({ kind: "assign", var: v, val: vars[v], srcToken: simpleToken(expr) });
6469
} else if (assign) {
6570
const [, v, expr] = assign;
6671
vars[v] = evalExpr(expr);
67-
trace.push({ kind: "assign", var: v, val: vars[v] });
72+
trace.push({ kind: "assign", var: v, val: vars[v], srcToken: simpleToken(expr) });
6873
}
6974
}
7075

71-
if (vars["z"] !== undefined) {
72-
trace.push({ kind: "print", val: vars["z"] });
73-
}
7476
return trace;
7577
}
7678

0 commit comments

Comments
 (0)