CoolFace
Apppublic

Nasim-Sami/Dynamic_Student_Simulator

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
dynamic_student_simulator.py1645 linesDownload Raw Back to root
1"""2Self-contained dynamic mechatronics MCQ student simulator.3 4This file intentionally does not import from student_simulator.py. It includes5its own question metadata, difficulty helpers, baseline student utilities, and6dynamic normal/boost/slump behavior so the dynamic env and trainer can be copied7as a standalone set.8"""9from __future__ import annotations10 11import math12from copy import deepcopy13from typing import Any14 15import numpy as np16 17 18OPTIONS = ["A", "B", "C", "D"]19MIN_ABILITY = 1020MAX_ABILITY = 3021DIFFICULTY_LOW = 0.022DIFFICULTY_HIGH = 16.023 24 25# ---------------------------------------------------------------------26# 1. Shuffled mechatronics question bank27# ---------------------------------------------------------------------28 29# The original pasted questions were ordered by inherent difficulty. The list30# below is deliberately shuffled so question index/order does not reveal the31# difficulty level during simulation or training. The question_id and32# ASSUMED_INHERENT_DIFFICULTY mapping still preserve the intended difficulty.33QUESTIONS_SHUFFLE_SEED = 2026060234 35QUESTIONS = [{'question_id': 'Q19',36  'question': 'A strain gauge has resistance 120 Ω and gauge factor 2.0. If strain is 1000 microstrain, the '37              'resistance change is approximately:',38  'option_A': '0.024 Ω',39  'option_B': '0.24 Ω',40  'option_C': '2.4 Ω',41  'option_D': '24 Ω',42  'answer': 'B',43  'distractor_strength': {'A': 2.1, 'C': 2.5, 'D': 1.1},44  'topic': 'Strain gauge',45  'subtopic': 'gauge factor calculation',46  'explanation': 'ΔR = GF × strain × R = 2 × 0.001 × 120 = 0.24 Ω.'},47 {'question_id': 'Q47',48  'question': 'A mechatronic positioning system reaches the target but vibrates around it. The sensor is '49              'accurate, and the actuator is strong. What is the most likely control-level issue?',50  'option_A': 'No physical target exists',51  'option_B': 'The system has too little supply voltage only',52  'option_C': 'The display is too bright',53  'option_D': 'Poor damping or excessive loop gain',54  'answer': 'D',55  'distractor_strength': {'A': 0.8, 'B': 1.6, 'C': 0.7},56  'topic': 'Closed-loop dynamic response',57  'subtopic': 'oscillation around setpoint',58  'explanation': 'Oscillation around a target often indicates insufficient damping or overly aggressive '59                 'control.'},60 {'question_id': 'Q14',61  'question': 'An inverting op-amp has input resistor 10 kΩ and feedback resistor 50 kΩ. If input voltage is '62              '0.2 V, what is the ideal output voltage?',63  'option_A': '-1.0 V',64  'option_B': '+1.0 V',65  'option_C': '+0.04 V',66  'option_D': '-0.04 V',67  'answer': 'A',68  'distractor_strength': {'B': 2.7, 'C': 1.2, 'D': 1.8},69  'topic': 'Operational amplifier',70  'subtopic': 'inverting amplifier',71  'explanation': 'Gain = -Rf/Rin = -50/10 = -5. Output = -5 × 0.2 = -1.0 V.'},72 {'question_id': 'Q4',73  'question': 'A rotary potentiometer is commonly used to measure:',74  'option_A': 'Shaft angle',75  'option_B': 'Fluid level',76  'option_C': 'Air pressure',77  'option_D': 'Light intensity',78  'answer': 'A',79  'distractor_strength': {'B': 1.7, 'C': 1.3, 'D': 1.1},80  'topic': 'Position sensors',81  'subtopic': 'rotary potentiometer',82  'explanation': 'A rotary potentiometer changes resistance as the shaft rotates, so it can measure angular '83                 'position.'},84 {'question_id': 'Q23',85  'question': 'Adding integral action to a controller mainly helps to:',86  'option_A': 'Increase sensor dead band',87  'option_B': 'Block actuator feedback',88  'option_C': 'Reduce controller memory',89  'option_D': 'Remove steady-state offset',90  'answer': 'D',91  'distractor_strength': {'A': 1.8, 'B': 1.2, 'C': 1.5},92  'topic': 'PI control',93  'subtopic': 'integral action',94  'explanation': 'Integral action accumulates error and can eliminate steady-state error.'},95 {'question_id': 'Q18',96  'question': 'A first-order temperature sensor reaches about 63% of its final value after:',97  'option_A': 'Two time constants',98  'option_B': 'Five time constants',99  'option_C': 'One time constant',100  'option_D': 'Ten time constants',101  'answer': 'C',102  'distractor_strength': {'A': 2.4, 'B': 1.5, 'D': 0.9},103  'topic': 'Dynamic response of sensors',104  'subtopic': 'time constant',105  'explanation': 'A first-order system reaches about 63.2% of its final response after one time constant.'},106 {'question_id': 'Q3',107  'question': 'A transducer is best described as a device that:',108  'option_A': 'Changes signal form',109  'option_B': 'Stores signal value',110  'option_C': 'Displays signal value',111  'option_D': 'Delays signal flow',112  'answer': 'A',113  'distractor_strength': {'B': 1.4, 'C': 1.8, 'D': 1.2},114  'topic': 'Sensors and transducers',115  'subtopic': 'transducer function',116  'explanation': 'A transducer converts information from one form into another, often from a physical effect '117                 'into an electrical signal.'},118 {'question_id': 'Q9',119  'question': 'A bimetallic strip bends when heated mainly because:',120  'option_A': 'One metal expands more than the other',121  'option_B': 'Both metals expand by exactly equal amounts',122  'option_C': 'One metal becomes electrically charged',123  'option_D': 'Both metals lose all mechanical stiffness',124  'answer': 'A',125  'distractor_strength': {'B': 2.4, 'C': 1.1, 'D': 1.4},126  'topic': 'Temperature sensors',127  'subtopic': 'bimetallic strip',128  'explanation': 'Different thermal expansion rates make the joined metals bend as temperature changes.'},129 {'question_id': 'Q36',130  'question': 'A controller gives fast response but large overshoot. Which action is commonly used to oppose '131              'rapid error change?',132  'option_A': 'Integral action',133  'option_B': 'Derivative action',134  'option_C': 'Offset action',135  'option_D': 'Dead-band action',136  'answer': 'B',137  'distractor_strength': {'A': 2.5, 'C': 1.1, 'D': 1.6},138  'topic': 'PID control',139  'subtopic': 'derivative action',140  'explanation': 'Derivative action responds to the rate of change of error and can reduce overshoot.'},141 {'question_id': 'Q46',142  'question': 'A sensor has excellent resolution but poor repeatability. What does this mean?',143  'option_A': 'It detects no changes but repeats perfectly',144  'option_B': 'It detects small changes but varies on repeats',145  'option_C': 'It has no hysteresis but large dead band',146  'option_D': 'It has high output but no input signal',147  'answer': 'B',148  'distractor_strength': {'A': 2.2, 'C': 1.8, 'D': 1.3},149  'topic': 'Sensor performance terminology',150  'subtopic': 'resolution and repeatability',151  'explanation': 'Resolution and repeatability are different. A sensor may detect fine changes but still '152                 'give inconsistent repeated readings.'},153 {'question_id': 'Q30',154  'question': 'A tachogenerator produces voltage proportional to angular velocity. If voltage polarity '155              'reverses, it most directly indicates:',156  'option_A': 'Increase of encoder resolution',157  'option_B': 'Reduction of winding resistance',158  'option_C': 'Loss of all feedback signal',159  'option_D': 'Reversal of rotation direction',160  'answer': 'D',161  'distractor_strength': {'A': 1.5, 'B': 1.2, 'C': 2.0},162  'topic': 'Velocity sensors',163  'subtopic': 'tachogenerator polarity',164  'explanation': 'The polarity of a tachogenerator output can indicate the direction of rotation.'},165 {'question_id': 'Q33',166  'question': 'In a DC motor model, back emf mainly depends on:',167  'option_A': 'Armature current',168  'option_B': 'Angular velocity',169  'option_C': 'Brush resistance only',170  'option_D': 'Gearbox ratio only',171  'answer': 'B',172  'distractor_strength': {'A': 2.4, 'C': 1.4, 'D': 1.7},173  'topic': 'Electromechanical systems',174  'subtopic': 'DC motor back emf',175  'explanation': 'Back emf is proportional to the motor angular velocity.'},176 {'question_id': 'Q7',177  'question': 'In an LVDT, when the core is exactly at the central position, the ideal output is:',178  'option_A': 'Maximum positive voltage',179  'option_B': 'Maximum negative voltage',180  'option_C': 'Nearly zero voltage',181  'option_D': 'Full supply voltage',182  'answer': 'C',183  'distractor_strength': {'A': 2.0, 'B': 2.0, 'D': 1.5},184  'topic': 'Displacement sensors',185  'subtopic': 'LVDT null position',186  'explanation': 'At the null position, the voltages induced in the two secondary coils cancel each other.'},187 {'question_id': 'Q32',188  'question': 'For a mass-spring-damper system with input force F and output displacement x, the transfer '189              'function X(s)/F(s) is:',190  'option_A': 'ms² + cs + k',191  'option_B': 's / (m + c + k)',192  'option_C': 'k / (ms + c)',193  'option_D': '1 / (ms² + cs + k)',194  'answer': 'D',195  'distractor_strength': {'A': 2.7, 'B': 1.4, 'C': 1.9},196  'topic': 'System transfer functions',197  'subtopic': 'mass-spring-damper model',198  'explanation': 'From mẍ + cẋ + kx = F, taking Laplace gives X/F = 1/(ms² + cs + k).'},199 {'question_id': 'Q17',200  'question': 'Cold-junction compensation is needed in thermocouple measurement because:',201  'option_A': 'The hot junction gives zero emf',202  'option_B': 'The metal wires act as switches',203  'option_C': 'The display creates the temperature',204  'option_D': 'The reference junction affects the emf',205  'answer': 'D',206  'distractor_strength': {'A': 2.0, 'B': 1.0, 'C': 1.2},207  'topic': 'Thermocouples',208  'subtopic': 'cold-junction compensation',209  'explanation': 'Thermocouple voltage depends on the temperature difference between hot and reference '210                 'junctions.'},211 {'question_id': 'Q5',212  'question': 'In an open-loop room heating system, which feature is normally absent?',213  'option_A': 'Heating element',214  'option_B': 'Power switching',215  'option_C': 'Temperature feedback',216  'option_D': 'Input command',217  'answer': 'C',218  'distractor_strength': {'A': 1.6, 'B': 1.9, 'D': 2.2},219  'topic': 'Open-loop and closed-loop control',220  'subtopic': 'open-loop heating',221  'explanation': 'An open-loop system acts without using measured output feedback for correction.'},222 {'question_id': 'Q16',223  'question': 'In a DC motor speed control system, a tachogenerator is mainly used to:',224  'option_A': 'Increase shaft friction',225  'option_B': 'Store motor position',226  'option_C': 'Feed back motor speed',227  'option_D': 'Limit armature length',228  'answer': 'C',229  'distractor_strength': {'A': 1.2, 'B': 2.1, 'D': 0.8},230  'topic': 'Closed-loop speed control',231  'subtopic': 'tachogenerator feedback',232  'explanation': 'A tachogenerator produces a voltage related to angular speed and is used for feedback.'},233 {'question_id': 'Q42',234  'question': 'A motor speed loop uses tachogenerator feedback. The tachogenerator polarity is accidentally '235              'reversed. What is the likely result?',236  'option_A': 'The system becomes more stable',237  'option_B': 'Negative feedback may become positive feedback, causing runaway or instability',238  'option_C': 'The motor cannot receive voltage',239  'option_D': 'The ADC becomes 16-bit automatically',240  'answer': 'B',241  'distractor_strength': {'A': 2.2, 'C': 1.3, 'D': 0.8},242  'topic': 'Feedback fault diagnosis',243  'subtopic': 'feedback polarity',244  'explanation': 'Wrong feedback polarity can make the controller increase the error instead of reducing '245                 'it.'},246 {'question_id': 'Q10',247  'question': 'A sensor has high sensitivity. Which statement is most accurate?',248  'option_A': 'Large input change gives no output change',249  'option_B': 'Small input change gives large output change',250  'option_C': 'Small output change gives large input change',251  'option_D': 'Output stays fixed for all input changes',252  'answer': 'B',253  'distractor_strength': {'A': 1.7, 'C': 2.7, 'D': 1.2},254  'topic': 'Sensor performance',255  'subtopic': 'sensitivity',256  'explanation': 'Sensitivity is the ratio of output change to input change.'},257 {'question_id': 'Q24',258  'question': 'In a sampled data system, sampling too slowly can cause:',259  'option_A': 'Hysteresis',260  'option_B': 'Dead band',261  'option_C': 'Aliasing',262  'option_D': 'Saturation',263  'answer': 'C',264  'distractor_strength': {'A': 1.8, 'B': 1.5, 'D': 1.6},265  'topic': 'Data acquisition',266  'subtopic': 'sampling',267  'explanation': 'Aliasing occurs when the sampling rate is too low for the signal being measured.'},268 {'question_id': 'Q12',269  'question': 'Mechanical switch bounce can cause a microprocessor to:',270  'option_A': 'Read analog voltage as temperature',271  'option_B': 'Convert input current into pressure',272  'option_C': 'Store the switch force permanently',273  'option_D': 'Count one press as many presses',274  'answer': 'D',275  'distractor_strength': {'A': 1.5, 'B': 1.1, 'C': 0.9},276  'topic': 'Switch debouncing',277  'subtopic': 'mechanical bounce',278  'explanation': 'Switch bounce produces rapid unwanted transitions before the signal settles.'},279 {'question_id': 'Q31',280  'question': 'In sequential control of a washing machine, why are water-level and door-position feedback '281              'important?',282  'option_A': 'They make every operation run at once',283  'option_B': 'They confirm safe conditions before the next step',284  'option_C': 'They remove the need for a controller',285  'option_D': 'They replace the motor and heater',286  'answer': 'B',287  'distractor_strength': {'A': 1.3, 'C': 1.7, 'D': 1.5},288  'topic': 'Sequential control',289  'subtopic': 'feedback conditions',290  'explanation': 'Sequential systems depend on feedback conditions before moving to the next operation.'},291 {'question_id': 'Q43',292  'question': 'A temperature control system has accurate sensor reading but poor final product temperature. '293              'Which hidden cause best fits a mechatronic-system view?',294  'option_A': 'Display brightness may not represent the control signal',295  'option_B': 'Switch timing may not represent the supply voltage',296  'option_C': 'Sensor position may not represent the product temperature',297  'option_D': 'Motor current may not represent the sensor reading',298  'answer': 'C',299  'distractor_strength': {'A': 1.7, 'B': 1.5, 'D': 2.0},300  'topic': 'Mechatronic system design',301  'subtopic': 'sensor placement',302  'explanation': 'A sensor may read accurately at its own location but still fail to represent the actual '303                 'controlled part of the process.'},304 {'question_id': 'Q37',305  'question': 'A multiplexed data acquisition system samples 8 sensors using one ADC. The main advantage is:',306  'option_A': 'Higher sensor accuracy always',307  'option_B': 'Zero sampling delay always',308  'option_C': 'No conditioning needed',309  'option_D': 'Lower hardware cost',310  'answer': 'D',311  'distractor_strength': {'A': 1.9, 'B': 1.8, 'C': 2.2},312  'topic': 'Data acquisition systems',313  'subtopic': 'multiplexing',314  'explanation': 'Multiplexing lets several input channels share one ADC.'},315 {'question_id': 'Q48',316  'question': 'A machine uses an encoder for position. After power loss, the controller no longer knows the '317              'shaft angle unless homing is performed. Which encoder type is most likely used?',318  'option_A': 'Absolute encoder',319  'option_B': 'Incremental encoder',320  'option_C': 'Thermocouple',321  'option_D': 'LVDT with no core',322  'answer': 'B',323  'distractor_strength': {'A': 2.8, 'C': 0.7, 'D': 1.1},324  'topic': 'Optical encoders',325  'subtopic': 'incremental encoder',326  'explanation': 'Incremental encoders count movement but do not inherently store absolute position after '327                 'power loss.'},328 {'question_id': 'Q11',329  'question': 'A strain gauge is usually connected in a bridge circuit mainly because:',330  'option_A': 'Its voltage output is naturally large',331  'option_B': 'Its current must always be zero',332  'option_C': 'Its resistance change is very small',333  'option_D': 'Its strain value is already digital',334  'answer': 'C',335  'distractor_strength': {'A': 1.9, 'B': 1.0, 'D': 1.3},336  'topic': 'Strain gauges',337  'subtopic': 'bridge circuit',338  'explanation': 'A bridge circuit helps detect small resistance changes caused by strain.'},339 {'question_id': 'Q22',340  'question': 'A proportional controller alone may leave steady-state error because:',341  'option_A': 'It stores all previous error values',342  'option_B': 'A nonzero error may be needed for output',343  'option_C': 'It reacts only to error acceleration',344  'option_D': 'It removes feedback from the loop',345  'answer': 'B',346  'distractor_strength': {'A': 2.0, 'C': 1.7, 'D': 1.2},347  'topic': 'Controllers',348  'subtopic': 'proportional control',349  'explanation': 'Pure proportional action often requires some remaining error to produce the needed control '350                 'effort.'},351 {'question_id': 'Q49',352  'question': 'A pneumatic actuator is fast but stops at slightly different positions under changing load. '353              'Which explanation is strongest?',354  'option_A': 'Pneumatic systems have no pressure',355  'option_B': 'Compressed air is perfectly rigid',356  'option_C': 'The actuator has infinite stiffness',357  'option_D': 'Air compressibility and load variation reduce precise positioning',358  'answer': 'D',359  'distractor_strength': {'A': 0.8, 'B': 2.2, 'C': 1.8},360  'topic': 'Pneumatic actuation',361  'subtopic': 'compressibility',362  'explanation': 'Compressed air is compressible, so position accuracy can suffer under load changes.'},363 {'question_id': 'Q38',364  'question': 'In a bridge measurement circuit, differential amplification is useful because it:',365  'option_A': 'Amplifies supply voltage and rejects sensor signal',366  'option_B': 'Amplifies resistance and rejects mechanical strain',367  'option_C': 'Amplifies difference and rejects common-mode signal',368  'option_D': 'Amplifies temperature and rejects bridge balance',369  'answer': 'C',370  'distractor_strength': {'A': 2.2, 'B': 1.8, 'D': 1.6},371  'topic': 'Signal conditioning',372  'subtopic': 'differential amplification',373  'explanation': 'Differential amplifiers are useful for small bridge outputs that contain common-mode '374                 'voltage.'},375 {'question_id': 'Q20',376  'question': 'In an LVDT with phase-sensitive demodulation, the output phase is useful because it '377              'indicates:',378  'option_A': 'Temperature of the winding',379  'option_B': 'Resistance of the core',380  'option_C': 'Direction of core movement',381  'option_D': 'Frequency of the supply only',382  'answer': 'C',383  'distractor_strength': {'A': 1.2, 'B': 1.5, 'D': 2.0},384  'topic': 'LVDT signal conditioning',385  'subtopic': 'phase-sensitive demodulation',386  'explanation': 'The phase changes depending on which side of the null position the core moves.'},387 {'question_id': 'Q13',388  'question': 'A Schmitt trigger is added before a microprocessor input. What is the main purpose?',389  'option_A': 'To increase actuator output torque',390  'option_B': 'To convert pressure into motion',391  'option_C': 'To sharpen noisy digital transitions',392  'option_D': 'To store sensor data permanently',393  'answer': 'C',394  'distractor_strength': {'A': 1.4, 'B': 1.1, 'D': 1.6},395  'topic': 'Digital input conditioning',396  'subtopic': 'Schmitt trigger',397  'explanation': 'A Schmitt trigger uses hysteresis to produce clean digital switching from noisy or slowly '398                 'changing input signals.'},399 {'question_id': 'Q28',400  'question': 'In a mass-spring-damper model, increasing damping usually causes:',401  'option_A': 'Higher overshoot tendency',402  'option_B': 'Lower oscillation tendency',403  'option_C': 'Larger steady vibration',404  'option_D': 'Smaller spring stiffness',405  'answer': 'B',406  'distractor_strength': {'A': 2.5, 'C': 1.9, 'D': 1.3},407  'topic': 'Dynamic response',408  'subtopic': 'damping',409  'explanation': 'Damping dissipates energy and reduces oscillatory behavior.'},410 {'question_id': 'Q1',411  'question': 'In a basic mechatronic measurement system, what is the main role of a sensor?',412  'option_A': 'To show the final reading',413  'option_B': 'To sense the input variable',414  'option_C': 'To amplify the output signal',415  'option_D': 'To drive the final actuator',416  'answer': 'B',417  'distractor_strength': {'A': 2.1, 'C': 2.4, 'D': 1.6},418  'topic': 'Sensors and transducers',419  'subtopic': 'sensor function',420  'explanation': 'A sensor responds to the input variable being measured and provides information for the '421                 'rest of the system.'},422 {'question_id': 'Q6',423  'question': 'A sensor gives the same output every time the same input is applied. Which performance term '424              'best describes this property?',425  'option_A': 'Sensitivity',426  'option_B': 'Repeatability',427  'option_C': 'Resolution',428  'option_D': 'Linearity',429  'answer': 'B',430  'distractor_strength': {'A': 2.2, 'C': 2.5, 'D': 1.9},431  'topic': 'Sensor performance terminology',432  'subtopic': 'repeatability',433  'explanation': 'Repeatability means the sensor gives nearly the same output for repeated identical input '434                 'conditions.'},435 {'question_id': 'Q15',436  'question': 'A 10-bit ADC is used over a 0–5 V range. The approximate voltage resolution is:',437  'option_A': '5.00 V',438  'option_B': '4.88 mV',439  'option_C': '0.50 V',440  'option_D': '10.0 mV',441  'answer': 'B',442  'distractor_strength': {'A': 0.8, 'C': 1.6, 'D': 2.4},443  'topic': 'Data acquisition',444  'subtopic': 'ADC resolution',445  'explanation': 'Resolution ≈ 5/1024 = 0.00488 V = 4.88 mV.'},446 {'question_id': 'Q26',447  'question': 'A hydraulic cylinder has piston area 0.002 m² and pressure 5 MPa. Ignoring losses, the force '448              'produced is:',449  'option_A': '100 N',450  'option_B': '1000 N',451  'option_C': '10000 N',452  'option_D': '25000 N',453  'answer': 'C',454  'distractor_strength': {'A': 0.9, 'B': 2.0, 'D': 2.2},455  'topic': 'Hydraulic actuation',456  'subtopic': 'pressure-force relation',457  'explanation': 'Force = pressure × area = 5,000,000 × 0.002 = 10,000 N.'},458 {'question_id': 'Q34',459  'question': 'In frequency response analysis, a small phase margin usually means the closed-loop system is:',460  'option_A': 'Closer to oscillation or instability',461  'option_B': 'Always slower but more stable',462  'option_C': 'Independent of loop gain',463  'option_D': 'Free from time delay effects',464  'answer': 'A',465  'distractor_strength': {'B': 2.1, 'C': 1.4, 'D': 1.8},466  'topic': 'Frequency response',467  'subtopic': 'phase margin',468  'explanation': 'Low phase margin means the system is closer to the boundary of instability.'},469 {'question_id': 'Q50',470  'question': 'A complete mechatronic product works in isolated tests: sensor test passes, actuator test '471              'passes, controller code passes. But the integrated system fails intermittently. What is the '472              'best systems explanation?',473  'option_A': 'Interfaces were not tested together',474  'option_B': 'Each part must work perfectly',475  'option_C': 'Mechanical parts cannot affect code',476  'option_D': 'Software cannot affect hardware',477  'answer': 'A',478  'distractor_strength': {'B': 2.1, 'C': 1.8, 'D': 1.7},479  'topic': 'Mechatronic system integration',480  'subtopic': 'subsystem interaction',481  'explanation': 'A full mechatronic system can fail because of timing, noise, loading, interface mismatch, '482                 'or subsystem interaction.'},483 {'question_id': 'Q39',484  'question': 'A closed-loop system becomes unstable after proportional gain is increased too much. The most '485              'likely reason is:',486  'option_A': 'Loop gain and phase lag reinforce oscillation',487  'option_B': 'Feedback automatically becomes zero',488  'option_C': 'Reference input removes the error',489  'option_D': 'The actuator stops receiving power',490  'answer': 'A',491  'distractor_strength': {'B': 1.2, 'C': 1.5, 'D': 1.0},492  'topic': 'Closed-loop stability',493  'subtopic': 'gain and phase lag',494  'explanation': 'High loop gain with enough phase lag can make a feedback system oscillate.'},495 {'question_id': 'Q45',496  'question': 'A PLC-controlled press must not operate unless two guard switches are closed. Which logic is '497              'safest for the enable condition?',498  'option_A': 'Guard1 AND Guard2',499  'option_B': 'Guard1 OR Guard2',500  'option_C': 'NOT Guard1',501  'option_D': 'Guard1 XOR Guard2',502  'answer': 'A',503  'distractor_strength': {'B': 2.4, 'C': 1.2, 'D': 2.0},504  'topic': 'PLC safety logic',505  'subtopic': 'AND condition',506  'explanation': 'Both guard switches must be true, so AND logic is required.'},507 {'question_id': 'Q2',508  'question': 'Which type of control system compares the actual output with the desired output?',509  'option_A': 'Open-loop control system',510  'option_B': 'Closed-loop control system',511  'option_C': 'Sequential timing system',512  'option_D': 'Manual switching system',513  'answer': 'B',514  'distractor_strength': {'A': 2.6, 'C': 1.5, 'D': 1.1},515  'topic': 'Control systems',516  'subtopic': 'closed-loop feedback',517  'explanation': 'A closed-loop system uses feedback to compare the measured output with the required '518                 'value.'},519 {'question_id': 'Q25',520  'question': 'A stepper motor has 200 full steps per revolution and a 5:1 reduction gearbox. What is the '521              'output shaft step angle?',522  'option_A': '1.8°',523  'option_B': '0.36°',524  'option_C': '5.0°',525  'option_D': '9.0°',526  'answer': 'B',527  'distractor_strength': {'A': 2.8, 'C': 1.2, 'D': 1.0},528  'topic': 'Electrical actuation',529  'subtopic': 'stepper motor resolution',530  'explanation': 'Motor step = 360/200 = 1.8°. With 5:1 reduction, output step = 1.8/5 = 0.36°.'},531 {'question_id': 'Q40',532  'question': 'A digital controller receives a noisy analog signal directly without filtering or protection. '533              'Which failure is most likely?',534  'option_A': 'Perfect control accuracy',535  'option_B': 'Automatic increase in sensor range',536  'option_C': 'Zero quantization error',537  'option_D': 'False or unstable control decisions',538  'answer': 'D',539  'distractor_strength': {'A': 0.9, 'B': 1.1, 'C': 1.7},540  'topic': 'Microprocessor interfacing',541  'subtopic': 'input signal protection',542  'explanation': 'Noisy input can cause the controller to act on incorrect measurements.'},543 {'question_id': 'Q44',544  'question': 'A sampled controller is stable in simulation with continuous equations but oscillates in '545              'hardware. Which overlooked factor is most suspicious?',546  'option_A': 'The existence of a reference input',547  'option_B': 'The use of a mechanical load',548  'option_C': 'The presence of a power supply',549  'option_D': 'Sampling delay and zero-order hold effects',550  'answer': 'D',551  'distractor_strength': {'A': 1.6, 'B': 2.0, 'C': 1.2},552  'topic': 'Digital control',553  'subtopic': 'sampling delay',554  'explanation': 'Real digital controllers introduce sampling and hold delays that affect stability.'},555 {'question_id': 'Q35',556  'question': 'A sensor has nonlinearity ±0.5% full scale and full-scale output 10 V. The maximum '557              'nonlinearity error is:',558  'option_A': '0.005 V',559  'option_B': '0.5 V',560  'option_C': '0.05 V',561  'option_D': '5 V',562  'answer': 'C',563  'distractor_strength': {'A': 2.3, 'B': 2.0, 'D': 0.9},564  'topic': 'Sensor error analysis',565  'subtopic': 'nonlinearity error',566  'explanation': '0.5% of 10 V = 0.005 × 10 = 0.05 V.'},567 {'question_id': 'Q8',568  'question': 'Why is Gray code often used in absolute optical encoders?',569  'option_A': 'It increases shaft speed',570  'option_B': 'It removes all sensor noise',571  'option_C': 'It changes one bit at a time',572  'option_D': 'It gives analog output directly',573  'answer': 'C',574  'distractor_strength': {'A': 1.1, 'B': 1.8, 'D': 2.1},575  'topic': 'Optical encoders',576  'subtopic': 'Gray code',577  'explanation': 'Gray code reduces transition error because only one bit changes between adjacent '578                 'positions.'},579 {'question_id': 'Q29',580  'question': 'A Schmitt trigger is useful in switch interfacing because it:',581  'option_A': 'Provides gain for hydraulic pressure',582  'option_B': 'Provides torque for motor starting',583  'option_C': 'Provides hysteresis for clean switching',584  'option_D': 'Provides storage for analog force',585  'answer': 'C',586  'distractor_strength': {'A': 1.1, 'B': 1.0, 'D': 1.3},587  'topic': 'Digital input conditioning',588  'subtopic': 'hysteresis',589  'explanation': 'A Schmitt trigger reduces false switching from noise or bouncing by using two threshold '590                 'levels.'},591 {'question_id': 'Q41',592  'question': 'A robotic gripper uses a force sensor. The measured force slowly drifts upward even when no '593              'object is held. The controller starts opening the gripper unnecessarily. What is the deepest '594              'design issue?',595  'option_A': 'The gripper has too many fingers',596  'option_B': 'The system lacks drift compensation or periodic zero calibration',597  'option_C': 'The actuator is definitely too weak',598  'option_D': 'The ADC resolution is always infinite',599  'answer': 'B',600  'distractor_strength': {'A': 0.9, 'C': 1.7, 'D': 1.1},601  'topic': 'Sensor fault and calibration',602  'subtopic': 'sensor drift',603  'explanation': 'Sensor drift can mislead feedback control unless calibration or compensation is included.'},604 {'question_id': 'Q27',605  'question': 'In PLC operation, the scan cycle usually means the PLC repeatedly:',606  'option_A': 'Reads inputs, solves logic, updates outputs',607  'option_B': 'Updates outputs, ignores inputs, stops logic',608  'option_C': 'Reads memory, clears program, blocks outputs',609  'option_D': 'Starts motor, resets timer, deletes counter',610  'answer': 'A',611  'distractor_strength': {'B': 2.0, 'C': 1.4, 'D': 1.1},612  'topic': 'Programmable logic controllers',613  'subtopic': 'PLC scan cycle',614  'explanation': 'A PLC scan commonly reads inputs, executes the program, and updates outputs.'},615 {'question_id': 'Q21',616  'question': 'A low-pass filter in signal conditioning is mainly used to:',617  'option_A': 'Reduce high-frequency noise',618  'option_B': 'Remove low-frequency signal',619  'option_C': 'Increase all frequency bands',620  'option_D': 'Convert frequency into force',621  'answer': 'A',622  'distractor_strength': {'B': 2.3, 'C': 1.6, 'D': 0.9},623  'topic': 'Filtering',624  'subtopic': 'low-pass filter',625  'explanation': 'A low-pass filter passes low-frequency components and attenuates high-frequency noise.'}]626 627 628# Inherent difficulty stays on the teacher-authored scale from the pasted629# mechatronics bank. Do not infer difficulty from list order.630ASSUMED_INHERENT_DIFFICULTY = {'Q1': 1.2,631 'Q2': 1.8,632 'Q3': 2.1,633 'Q4': 2.5,634 'Q5': 2.9,635 'Q6': 3.1,636 'Q7': 3.4,637 'Q8': 3.7,638 'Q9': 3.9,639 'Q10': 4.7,640 'Q11': 4.4,641 'Q12': 4.6,642 'Q13': 4.9,643 'Q14': 5.1,644 'Q15': 5.3,645 'Q16': 5.5,646 'Q17': 5.7,647 'Q18': 5.9,648 'Q19': 6.1,649 'Q20': 6.2,650 'Q21': 6.4,651 'Q22': 6.5,652 'Q23': 6.7,653 'Q24': 6.9,654 'Q25': 7.1,655 'Q26': 7.2,656 'Q27': 7.3,657 'Q28': 7.4,658 'Q29': 7.6,659 'Q30': 7.8,660 'Q31': 7.9,661 'Q32': 8.1,662 'Q33': 8.2,663 'Q34': 8.3,664 'Q35': 8.4,665 'Q36': 8.5,666 'Q37': 8.6,667 'Q38': 8.7,668 'Q39': 8.8,669 'Q40': 8.9,670 'Q41': 9.1,671 'Q42': 9.2,672 'Q43': 9.3,673 'Q44': 9.4,674 'Q45': 9.5,675 'Q46': 9.6,676 'Q47': 9.7,677 'Q48': 9.8,678 'Q49': 9.9,679 'Q50': 10.0}680 681 682# ---------------------------------------------------------------------683# 3. Utility functions684# ---------------------------------------------------------------------685 686def sigmoid(x: float) -> float:687    return 1.0 / (1.0 + math.exp(-x))688 689 690def clip(value: float, low: float, high: float) -> float:691    return float(max(low, min(high, value)))692 693 694def ability_to_10_scale(ability: int | float) -> float:695    """696    Convert ability from 10-30 scale into 0-10 scale.697 698    Why?699    - This experiment is designed for students with ability 10-30.700    - This scale is kept for reporting/backward compatibility.701    - Do not use this for direct comparison with perceived difficulty.702    """703    ability = clip(float(ability), MIN_ABILITY, MAX_ABILITY)704    return ((ability - MIN_ABILITY) / (MAX_ABILITY - MIN_ABILITY)) * 10.0705 706 707def ability_to_difficulty_scale(ability: int | float) -> float:708    """709    Convert ability from 10-30 scale into the perceived-difficulty scale.710 711    Perceived difficulty is sampled from DIFFICULTY_LOW..DIFFICULTY_HIGH,712    currently 0..15. This is the scale used for answer probability and713    response-time simulation.714    """715    ability = clip(float(ability), MIN_ABILITY, MAX_ABILITY)716    ability_norm = (ability - MIN_ABILITY) / (MAX_ABILITY - MIN_ABILITY)717    return DIFFICULTY_LOW + ability_norm * (DIFFICULTY_HIGH - DIFFICULTY_LOW)718 719 720def make_default_distractor_strength(correct_answer: str) -> dict[str, float]:721    """722    If you do not manually give distractor strengths,723    all wrong options are treated as equally attractive.724    """725    return {option: 1.0 for option in OPTIONS if option != correct_answer}726 727 728# ---------------------------------------------------------------------729# 4. Build difficulty profile for ability 10-30730# ---------------------------------------------------------------------731 732def build_difficulty_profile(inherent_difficulty: float) -> dict[int, dict[str, float]]:733    """734    Creates:735    {736        10: {"center": ..., "spread": ...},737        11: {"center": ..., "spread": ...},738        ...739        30: {"center": ..., "spread": ...},740    }741 742    center:743        Most likely perceived difficulty for that ability.744 745    spread:746        How much the sampled difficulty varies around the center.747 748    Important:749        In this experiment, ability=10 is the weakest supported student and750        ability=30 is the strongest. The model is not trained for absolute751        beginner behavior below ability 10.752    """753 754    # Mechatronics questions contain more technical terminology and multistep755    # reasoning, so perceived difficulty should climb more sharply for hard756    # questions while still preserving separation among easier questions.757    high_difficulty_excess = max(0.0, inherent_difficulty - 7.0)758    low_ability_center = clip(759        1.24 * inherent_difficulty + 1.15 + 0.55 * high_difficulty_excess + 0.10 * high_difficulty_excess**2,760        1.2,761        14.8,762    )763    high_ability_center = clip(764        0.52 * inherent_difficulty + 0.55 + 0.24 * high_difficulty_excess,765        0.8,766        7.4,767    )768 769    profile: dict[int, dict[str, float]] = {}770 771    for ability in range(MIN_ABILITY, MAX_ABILITY + 1):772        t = (ability - MIN_ABILITY) / (MAX_ABILITY - MIN_ABILITY)773 774        # Linear interpolation from low ability center to high ability center.775        center = low_ability_center * (1.0 - t) + high_ability_center * t776 777        # Hard technical questions should have a little more uncertainty in778        # perceived difficulty, because students can know one sub-concept but779        # still struggle with another.780        spread = 0.32 + 0.045 * inherent_difficulty + 0.035 * high_difficulty_excess + 0.018 * abs(center - 6.0)781        spread = clip(spread, 0.32, 1.12)782 783        profile[ability] = {784            "center": round(center, 2),785            "spread": round(spread, 2),786        }787 788    return profile789 790 791def estimate_base_time(question: dict[str, Any], inherent_difficulty: float) -> float:792    """793    Assumed base response time in seconds.794 795    Higher difficulty + longer text => more base time.796    """797    text = (798        question["question"]799        + " "800        + question["option_A"]801        + " "802        + question["option_B"]803        + " "804        + question["option_C"]805        + " "806        + question["option_D"]807    )808 809    length_factor = min(len(text) / 70.0, 10.0)810    high_difficulty_excess = max(0.0, inherent_difficulty - 7.0)811    high_difficulty_time = 4.0 * (high_difficulty_excess ** 1.45)812    advanced_reasoning_time = 2.0 * (max(0.0, inherent_difficulty - 8.5) ** 2)813    base_time = (814        12.0815        + inherent_difficulty * 4.1816        + length_factor817        + high_difficulty_time818        + advanced_reasoning_time819    )820 821    return round(base_time, 2)822 823 824def attach_assumed_question_metadata(raw_questions: list[dict[str, Any]]) -> list[dict[str, Any]]:825    """826    Adds:827    - inherent_difficulty828    - base_time829    - difficulty_profile830    - distractor_strength831    """832    questions = deepcopy(raw_questions)833 834    for question in questions:835        qid = question["question_id"]836 837        inherent_difficulty = ASSUMED_INHERENT_DIFFICULTY[qid]838 839        question["inherent_difficulty"] = inherent_difficulty840        question["base_time"] = estimate_base_time(question, inherent_difficulty)841        question["difficulty_profile"] = build_difficulty_profile(inherent_difficulty)842 843        if "distractor_strength" not in question:844            question["distractor_strength"] = make_default_distractor_strength(question["answer"])845 846    return questions847 848 849# This is the final question list you should use in simulation.850QUESTIONS_WITH_METADATA = attach_assumed_question_metadata(QUESTIONS)851 852 853# ---------------------------------------------------------------------854# 5. Difficulty sampling855# ---------------------------------------------------------------------856 857def sample_difficulty_from_distribution(858    center: float,859    spread: float,860    rng: np.random.Generator,861    low: float = DIFFICULTY_LOW,862    high: float = DIFFICULTY_HIGH,863    step: float = 0.1,864) -> float:865    """866    Discrete bell-shaped distribution.867 868    Highest probability is near 'center'.869    Probability decreases on both left and right sides.870    """871    values = np.round(np.arange(low, high + step, step), 2)872 873    weights = np.exp(-0.5 * ((values - center) / spread) ** 2)874    probabilities = weights / weights.sum()875 876    sampled = rng.choice(values, p=probabilities)877    return float(sampled)878 879 880def sample_perceived_difficulty(881    student: dict[str, Any],882    question: dict[str, Any],883    rng: np.random.Generator,884) -> float:885    """886    Picks the difficulty profile row for the student's ability,887    then samples one perceived difficulty value.888    """889    ability = int(student["ability"])890 891    if ability < MIN_ABILITY or ability > MAX_ABILITY:892        raise ValueError(f"Student ability must be between {MIN_ABILITY} and {MAX_ABILITY}. Got {ability}.")893 894    params = question["difficulty_profile"][ability]895    center = params["center"]896    spread = params["spread"]897 898    return sample_difficulty_from_distribution(center=center, spread=spread, rng=rng)899 900 901# ---------------------------------------------------------------------902# 6. Option distribution903# ---------------------------------------------------------------------904 905def get_option_distribution(906    student: dict[str, Any],907    question: dict[str, Any],908    perceived_difficulty: float,909) -> dict[str, float]:910    """911    Returns probability of choosing each option.912 913    Example:914    {915        "A": 0.70,916        "B": 0.10,917        "C": 0.12,918        "D": 0.08,919    }920    """921 922    ability_difficulty_scale = ability_to_difficulty_scale(student["ability"])923    correct = question["answer"]924 925    # If ability on the same scale is greater than perceived difficulty,926    # mastery becomes high.927    mastery = sigmoid(0.85 * (ability_difficulty_scale - perceived_difficulty))928 929    # Random guessing floor for 4-option MCQ.930    min_correct = 1.0 / len(OPTIONS)931 932    # Even a strong student can make careless mistakes.933    carelessness = float(student.get("carelessness", 0.05))934    max_correct = clip(0.98 - carelessness, min_correct, 0.98)935 936    p_correct = min_correct + (max_correct - min_correct) * mastery937    p_correct = clip(p_correct, min_correct, max_correct)938 939    distribution: dict[str, float] = {}940    distribution[correct] = p_correct941 942    wrong_probability = 1.0 - p_correct943 944    distractors = question.get("distractor_strength") or make_default_distractor_strength(correct)945 946    # Safety: only wrong options should be in distractors.947    distractors = {opt: strength for opt, strength in distractors.items() if opt != correct}948 949    total_strength = sum(distractors.values())950 951    if total_strength <= 0:952        distractors = make_default_distractor_strength(correct)953        total_strength = sum(distractors.values())954 955    for option, strength in distractors.items():956        distribution[option] = wrong_probability * (strength / total_strength)957 958    # Ensure all options exist.959    for option in OPTIONS:960        distribution.setdefault(option, 0.0)961 962    # Normalize for numerical safety.963    total = sum(distribution.values())964    distribution = {option: prob / total for option, prob in distribution.items()}965 966    return distribution967 968 969# ---------------------------------------------------------------------970# 7. Response time sampling971# ---------------------------------------------------------------------972 973def sample_response_time(974    student: dict[str, Any],975    question: dict[str, Any],976    perceived_difficulty: float,977    rng: np.random.Generator,978) -> float:979    """980    Generates different time every time, even for same student + same question.981 982    Uses same sampled perceived_difficulty that was used for answer probability.983    """984 985    base_time = float(question["base_time"])986    ability_difficulty_scale = ability_to_difficulty_scale(student["ability"])987 988    time_multiplier = float(student.get("time_multiplier", 1.0))989 990    difficulty_gap = perceived_difficulty - ability_difficulty_scale991    high_difficulty_excess = max(0.0, float(question["inherent_difficulty"]) - 7.0)992 993    # Technical struggle has a stronger effect on response time than in the994    # English grammar simulator.995    struggle_factor = 1.0 + max(0.0, difficulty_gap) * 0.125 + high_difficulty_excess * 0.045996 997    # Even strong students still need reading/reasoning time for technical MCQ.998    ease_factor = 1.0 - min(max(0.0, ability_difficulty_scale - perceived_difficulty) * 0.014, 0.20)999 1000    min_time = max(3.0, base_time * time_multiplier * 0.38 * ease_factor)1001    max_time = max(1002        min_time + 2.0,1003        base_time * time_multiplier * (2.10 + high_difficulty_excess * 0.12) * struggle_factor,1004    )1005 1006    alpha = float(student.get("time_alpha", 2.5))1007    beta = float(student.get("time_beta", 4.0))1008 1009    x = rng.beta(alpha, beta)1010 1011    time_taken = min_time + x * (max_time - min_time)1012 1013    return round(float(time_taken), 2)1014 1015 1016# ---------------------------------------------------------------------1017# 8. Student creation1018# ---------------------------------------------------------------------1019 1020def create_student(1021    student_id: str,1022    ability: int,1023    carelessness: float | None = None,1024    time_multiplier: float | None = None,1025    time_alpha: float | None = None,1026    time_beta: float | None = None,1027) -> dict[str, Any]:1028    """1029    Create one simulated student.1030 1031    ability:1032        10 = weakest supported student in this experiment1033        30 = strongest supported student in this experiment1034    """1035 1036    if ability < MIN_ABILITY or ability > MAX_ABILITY:1037        raise ValueError(f"ability must be between {MIN_ABILITY} and {MAX_ABILITY}")1038 1039    ability_10 = ability_to_10_scale(ability)1040    ability_difficulty_scale = ability_to_difficulty_scale(ability)1041 1042    if carelessness is None:1043        # Stronger students are usually less careless, but not zero.1044        carelessness = clip(0.16 - ability_10 * 0.011, 0.025, 0.16)1045 1046    if time_multiplier is None:1047        # Stronger students are usually faster.1048        time_multiplier = clip(1.35 - ability_10 * 0.055, 0.65, 1.35)1049 1050    if time_alpha is None:1051        # Lower alpha / higher beta means values tend to be closer to min_time.1052        time_alpha = clip(2.2 + ability_10 * 0.06, 2.0, 3.0)1053 1054    if time_beta is None:1055        time_beta = clip(3.2 + ability_10 * 0.10, 3.0, 4.5)1056 1057    return {1058        "student_id": student_id,1059        "ability": int(ability),1060        "ability_10": round(ability_10, 2),1061        "ability_difficulty_scale": round(ability_difficulty_scale, 2),1062        "carelessness": round(float(carelessness), 3),1063        "time_multiplier": round(float(time_multiplier), 3),1064        "time_alpha": round(float(time_alpha), 3),1065        "time_beta": round(float(time_beta), 3),1066    }1067 1068 1069def create_student_population(1070    variants_per_ability: int = 1,1071    seed: int | None = None,1072    student_id_prefix: str = "S",1073) -> list[dict[str, Any]]:1074    """Create varied student profiles across the supported ability range.1075 1076    Ability controls the main knowledge level. Variants add small differences in1077    carelessness and speed so the model does not see only one personality for a1078    given ability.1079    """1080 1081    if variants_per_ability < 1:1082        raise ValueError("variants_per_ability must be at least 1")1083 1084    rng = np.random.default_rng(seed)1085    population: list[dict[str, Any]] = []1086 1087    for ability in range(MIN_ABILITY, MAX_ABILITY + 1):1088        base = create_student(f"{student_id_prefix}{ability:02d}", ability=ability)1089 1090        if variants_per_ability == 1:1091            population.append(base)1092            continue1093 1094        for variant in range(1, variants_per_ability + 1):1095            carelessness = clip(1096                float(base["carelessness"]) + float(rng.normal(0.0, 0.025)),1097                0.02,1098                0.22,1099            )1100            time_multiplier = clip(1101                float(base["time_multiplier"]) * float(rng.lognormal(mean=0.0, sigma=0.10)),1102                0.55,1103                1.65,1104            )1105            time_alpha = clip(1106                float(base["time_alpha"]) + float(rng.normal(0.0, 0.12)),1107                1.7,1108                3.4,1109            )1110            time_beta = clip(1111                float(base["time_beta"]) + float(rng.normal(0.0, 0.15)),1112                2.5,1113                5.0,1114            )1115 1116            population.append(1117                create_student(1118                    f"{student_id_prefix}{ability:02d}_v{variant:02d}",1119                    ability=ability,1120                    carelessness=carelessness,1121                    time_multiplier=time_multiplier,1122                    time_alpha=time_alpha,1123                    time_beta=time_beta,1124                )1125            )1126 1127    return population1128 1129 1130# ---------------------------------------------------------------------1131# 9. Simulate answer1132# ---------------------------------------------------------------------1133 1134def simulate_answer(1135    student: dict[str, Any],1136    question: dict[str, Any],1137    rng: np.random.Generator | None = None,1138) -> dict[str, Any]:1139    """1140    Simulates one student answering one question.1141    """1142 1143    if rng is None:1144        rng = np.random.default_rng()1145 1146    perceived_difficulty = sample_perceived_difficulty(student, question, rng)1147 1148    option_distribution = get_option_distribution(1149        student=student,1150        question=question,1151        perceived_difficulty=perceived_difficulty,1152    )1153 1154    probs = [option_distribution[opt] for opt in OPTIONS]1155 1156    chosen_option = str(rng.choice(OPTIONS, p=probs))1157    time_taken = sample_response_time(1158        student=student,1159        question=question,1160        perceived_difficulty=perceived_difficulty,1161        rng=rng,1162    )1163 1164    is_correct = chosen_option == question["answer"]1165 1166    return {1167        "student_id": student["student_id"],1168        "student_ability": student["ability"],1169        "student_ability_10": student.get("ability_10", ability_to_10_scale(student["ability"])),1170        "student_ability_difficulty_scale": student.get(1171            "ability_difficulty_scale",1172            ability_to_difficulty_scale(student["ability"]),1173        ),1174 1175        "question_id": question["question_id"],1176        "question": question["question"],1177        "topic": question["topic"],1178        "subtopic": question["subtopic"],1179 1180        "inherent_difficulty": question["inherent_difficulty"],1181        "base_time": question["base_time"],1182        "sampled_perceived_difficulty": perceived_difficulty,1183 1184        "chosen_option": chosen_option,1185        "correct_answer": question["answer"],1186        "is_correct": is_correct,1187        "time_taken": time_taken,1188 1189        "option_distribution": option_distribution,1190    }1191 1192 1193def simulate_dataset(1194    students: list[dict[str, Any]],1195    questions: list[dict[str, Any]],1196    attempts_per_student_question: int = 1,1197    seed: int = 42,1198) -> list[dict[str, Any]]:1199    """1200    Simulate many student-question interactions.

Showing the first 1,200 of 1645 lines. Download the file for the rest.