@@ -33,11 +33,12 @@ def _check_minimality(fsa: FSA) -> Tuple[bool, Optional[ValidationError]]:
3333 try :
3434 minimized = hopcroft_minimization (fsa )
3535 if len (minimized .states ) < len (fsa .states ):
36+ diff = len (fsa .states ) - len (minimized .states )
3637 return False , ValidationError (
37- message = f"FSA is not minimal: has { len (fsa .states )} states but can be reduced to { len (minimized .states )} " ,
38+ message = f"Your FSA works correctly, but it's not minimal! You have { len (fsa .states )} states, but only { len (minimized .states )} are needed. You could remove { diff } state(s). " ,
3839 code = ErrorCode .NOT_MINIMAL ,
3940 severity = "error" ,
40- suggestion = "Minimize your FSA by merging equivalent states "
41+ suggestion = "Look for states that behave identically (same transitions and acceptance) - these can be merged into one "
4142 )
4243 return True , None
4344 except Exception :
@@ -69,9 +70,11 @@ def _build_feedback(
6970 hints = [e .suggestion for e in all_errors if e .suggestion ]
7071 if structural_info :
7172 if structural_info .unreachable_states :
72- hints .append ("Consider removing unreachable states" )
73+ unreachable = ", " .join (structural_info .unreachable_states )
74+ hints .append (f"Tip: States {{{ unreachable } }} can't be reached from your start state - you might want to remove them or add transitions to them" )
7375 if structural_info .dead_states :
74- hints .append ("Dead states can never lead to acceptance" )
76+ dead = ", " .join (structural_info .dead_states )
77+ hints .append (f"Tip: States {{{ dead } }} can never lead to acceptance - this might be intentional (trap states) or a bug" )
7578
7679 # Build language comparison
7780 language = LanguageComparison (are_equivalent = len (equivalence_errors ) == 0 )
@@ -92,17 +95,20 @@ def _summarize_errors(errors: List[ValidationError]) -> str:
9295 for error in errors :
9396 msg = error .message .lower ()
9497 if "alphabet" in msg :
95- error_types .add ("alphabet mismatch " )
96- elif "state " in msg and "count " in msg :
97- error_types .add ("state count mismatch " )
98- elif "accepting" in msg or "incorrectly marked " in msg :
99- error_types .add ("acceptance error " )
100- elif "transition" in msg :
101- error_types .add ("transition error " )
98+ error_types .add ("alphabet issue " )
99+ elif "states " in msg and ( "many " in msg or "few" in msg or "needed" in msg ) :
100+ error_types .add ("incorrect number of states " )
101+ elif "accepting" in msg or "accept " in msg :
102+ error_types .add ("accepting states issue " )
103+ elif "transition" in msg or "reading" in msg :
104+ error_types .add ("transition issue " )
102105
103- if error_types :
104- return f"Languages differ: { ', ' .join (error_types )} "
105- return f"Languages differ: { len (errors )} issue(s)"
106+ if len (error_types ) == 1 :
107+ issue = list (error_types )[0 ]
108+ return f"Almost there! Your FSA has an { issue } . Check the details below."
109+ elif error_types :
110+ return f"Your FSA doesn't quite match the expected language. Issues found: { ', ' .join (error_types )} "
111+ return f"Your FSA doesn't accept the correct language. Found { len (errors )} issue(s) to fix."
106112
107113
108114# =============================================================================
@@ -134,7 +140,11 @@ def analyze_fsa_correction(
134140 # Step 1: Validate student FSA structure
135141 student_errors = is_valid_fsa (student_fsa )
136142 if student_errors :
137- summary = "FSA has structural errors"
143+ num_errors = len (student_errors )
144+ if num_errors == 1 :
145+ summary = "Your FSA has a structural problem that needs to be fixed first. See the details below."
146+ else :
147+ summary = f"Your FSA has { num_errors } structural problems that need to be fixed first. See the details below."
138148 return Result (
139149 is_correct = False ,
140150 feedback = summary ,
@@ -146,7 +156,7 @@ def analyze_fsa_correction(
146156 if expected_errors :
147157 return Result (
148158 is_correct = False ,
149- feedback = "Internal error: expected FSA is invalid "
159+ feedback = "Oops! There's an issue with the expected answer. Please contact your instructor. "
150160 )
151161
152162 # Step 3: Check minimality if required
@@ -162,15 +172,18 @@ def analyze_fsa_correction(
162172 equivalence_errors = fsas_accept_same_language (student_fsa , expected_fsa )
163173
164174 if not equivalence_errors and not validation_errors :
175+ # Success message with some stats
176+ state_count = len (student_fsa .states )
177+ feedback = f"Correct! Your FSA with { state_count } state(s) accepts exactly the right language. Well done!"
165178 return Result (
166179 is_correct = True ,
167- feedback = "Correct! FSA accepts the expected language." ,
168- fsa_feedback = _build_feedback ("FSA is correct" , [], [], structural_info )
180+ feedback = feedback ,
181+ fsa_feedback = _build_feedback ("Your FSA is correct! " , [], [], structural_info )
169182 )
170183
171184 # Build result with errors
172185 is_correct = len (equivalence_errors ) == 0 and len (validation_errors ) == 0
173- summary = _summarize_errors (equivalence_errors ) if equivalence_errors else "FSA has issues"
186+ summary = _summarize_errors (equivalence_errors ) if equivalence_errors else "Your FSA has some issues to address. "
174187
175188 return Result (
176189 is_correct = is_correct ,
0 commit comments