elemes/frontend/src/lib/services/exercise.ts

50 lines
1.6 KiB
TypeScript

/**
* Pure helper functions for exercise evaluation.
*/
import type { CircuitJSApi } from '$types/circuitjs';
/** Return key_text keywords (one per line) that are missing from the code. */
export function missingKeyTextKeys(code: string, keyText: string): string[] {
if (!keyText.trim()) return [];
const keys = keyText.split('\n').map(k => k.trim()).filter(k => k.length > 0);
return keys.filter(key => !code.includes(key));
}
/** Check if code contains all required key_text keywords (one per line). */
export function checkKeyText(code: string, keyText: string): boolean {
return missingKeyTextKeys(code, keyText).length === 0;
}
export interface NodeResult {
passed: boolean;
message: string;
}
/** Validate circuit node voltages against expected state. */
export function validateNodes(
simApi: CircuitJSApi,
nodes: Record<string, { voltage: number; tolerance?: number }>
): { allPassed: boolean; messages: string[] } {
let allPassed = true;
const messages: string[] = [];
for (const [nodeName, criteria] of Object.entries(nodes)) {
const actualV = simApi.getNodeVoltage(nodeName);
if (actualV === undefined || actualV === null) {
allPassed = false;
messages.push(`❌ Node '${nodeName}' tidak ditemukan.`);
continue;
}
const tol = criteria.tolerance || 0.1;
if (Math.abs(actualV - criteria.voltage) <= tol) {
messages.push(`✅ Node '${nodeName}': Tegangan ${actualV.toFixed(2)}V (Sesuai)`);
} else {
allPassed = false;
messages.push(`❌ Node '${nodeName}': Tegangan ${actualV.toFixed(2)}V (Harusnya ~${criteria.voltage}V)`);
}
}
return { allPassed, messages };
}