.data slotsTestStage3D dd 0 slotsTest0 db "build\actions\slots-ready.bmp",0 slotsTest1 db "build\actions\slots-600.bmp",0 slotsTest2 db "build\actions\slots-1799.bmp",0 slotsTest3 db "build\actions\slots-1800.bmp",0 slotsTest4 db "build\actions\slots-2400.bmp",0 slotsTest5 db "build\actions\slots-win.bmp",0 slotsTest6 db "build\actions\slots-paytable.bmp",0 slotsTest7 db "build\actions\slots-exit.bmp",0 slotsTestTimes dd 600,1799,1800,2400 slotsTestPaths dd OFFSET slotsTest1,OFFSET slotsTest2,OFFSET slotsTest3,OFFSET slotsTest4 slotsTestRates dd 16,33,50,250 .code RunSlotsTest3D PROC uses ebx esi edi local oldBank:DWORD,oldMode:DWORD,oldSeed:DWORD,oldX:DWORD,oldY:DWORD,oldAngle:DWORD local total:DWORD,iterations:DWORD,expected:DWORD,rngSnapshot:DWORD,ticks:DWORD local previous:DWORD,reel:DWORD,timer:DWORD,first:DWORD,second:DWORD,third:DWORD mov eax,bankroll mov oldBank,eax mov eax,engineMode mov oldMode,eax mov eax,rngSeed3D mov oldSeed,eax mov eax,playerX mov oldX,eax mov eax,playerY mov oldY,eax mov eax,playerAngle mov oldAngle,eax mov bankroll,100000 mov slotsTestStage3D,1 mov engineMode,MODE_WALK mov interactTargetId,INTERACT_SLOTS_777 call Trigger3DInteraction cmp engineMode,MODE_SLOTS jne slots_test_fail cmp bankroll,100000 jne slots_test_fail cmp slotSpinState,SLOT_READY jne slots_test_fail call TableBusy3D test eax,eax jnz slots_test_fail call RenderNativeSlots3D invoke WriteActionSnapshot3D,OFFSET slotsTest0 test eax,eax jz slots_test_fail ; Invalid dimensions fail closed, with no charged stake or loaded remnants. mov slotSymbolsPack3D.frames,7 call InitNativeSlots3D test eax,eax jnz slots_test_fail cmp slotSymbolsPack3D.pixels,0 jne slots_test_fail cmp slotCabinetPack3D.pixels,0 jne slots_test_fail call StartNativeSlots3D cmp bankroll,100000 jne slots_test_fail mov BYTE PTR slotSymbolsPath3D,'!' call InitNativeSlots3D mov BYTE PTR slotSymbolsPath3D,'a' test eax,eax jnz slots_test_fail cmp slotCabinetPack3D.pixels,0 jne slots_test_fail call InitNativeSlots3D test eax,eax jz slots_test_fail mov slotBet,0 call StartNativeSlots3D cmp slotSpinState,SLOT_READY jne slots_test_fail mov slotBet,10001 call StartNativeSlots3D cmp bankroll,100000 jne slots_test_fail mov bankroll,49 mov slotBet,50 mov eax,rngSeed3D mov rngSnapshot,eax call StartNativeSlots3D cmp bankroll,49 jne slots_test_fail mov eax,rngSnapshot cmp rngSeed3D,eax jne slots_test_fail cmp slotError3D,1 jne slots_test_fail ; Every possible 20^3 stop combination exercises the actual MASM evaluator. mov slotsTestStage3D,2 mov total,0 xor ebx,ebx slots_test_first: xor esi,esi slots_test_second: xor edi,edi slots_test_third: mov eax,slotStrip3D[ebx*4] mov first,eax mov eax,slotStrip3D[esi*4] mov second,eax mov eax,slotStrip3D[edi*4] mov third,eax invoke SlotMultiplier3D,first,second,third add total,eax mov expected,eax mov ecx,first cmp ecx,second jne slots_test_pair_other cmp ecx,third jne slots_test_pair mov eax,slotTriple3D[ecx*4] jmp slots_test_compare slots_test_pair_other: cmp ecx,third je slots_test_pair mov ecx,second cmp ecx,third jne slots_test_zero slots_test_pair: xor eax,eax cmp ecx,1 sete al inc eax jmp slots_test_compare slots_test_zero: xor eax,eax slots_test_compare: cmp eax,expected jne slots_test_fail inc edi cmp edi,20 jb slots_test_third inc esi cmp esi,20 jb slots_test_second inc ebx cmp ebx,20 jb slots_test_first cmp total,7202 jne slots_test_fail invoke SlotMultiplier3D,8,8,8 test eax,eax jnz slots_test_fail ; Five hundred complete paid spins at four update rates, no double debit/pay. mov slotsTestStage3D,3 mov iterations,500 slots_test_round: mov slotSpinState,SLOT_READY mov slotBet,100 mov slotPaytable3D,0 mov bankroll,100000 call StartNativeSlots3D cmp bankroll,99900 jne slots_test_fail cmp slotSpinState,SLOT_SPIN jne slots_test_fail mov eax,rngSeed3D mov rngSnapshot,eax call StartNativeSlots3D cmp bankroll,99900 jne slots_test_fail mov eax,rngSnapshot cmp rngSeed3D,eax jne slots_test_fail invoke SlotMultiplier3D,slotReel1,slotReel2,slotReel3 imul eax,100 add eax,99900 mov expected,eax mov eax,iterations and eax,3 mov eax,slotsTestRates[eax*4] mov deltaTime3D,eax mov ticks,0 slots_test_round_tick: ; Spinning ignores wager and start input, rather than queueing it for later. mov pressedKeys3D[57h],1 mov pressedKeys3D[20h],1 call HandleNativeSlots3D call ClearKeys3D cmp slotBet,100 jne slots_test_fail mov eax,rngSnapshot cmp rngSeed3D,eax jne slots_test_fail inc ticks cmp ticks,200 ja slots_test_fail cmp slotSpinState,SLOT_SPIN je slots_test_round_tick mov eax,expected cmp bankroll,eax jne slots_test_fail call SettleNativeSlots3D mov eax,expected cmp bankroll,eax jne slots_test_fail xor esi,esi slots_test_final_reel: invoke SlotReelPosition3D,esi shr eax,6 cmp eax,slotTargets3D[esi*4] jne slots_test_fail inc esi cmp esi,3 jb slots_test_final_reel dec iterations jnz slots_test_round ; The same millisecond timestamp gives the same strictly nondecreasing path. mov slotsTestStage3D,4 mov slotSpinState,SLOT_SPIN mov reel,0 slots_test_monotonic_reel: mov timer,0 mov previous,0 slots_test_monotonic_tick: mov eax,timer mov slotSpinTimer,eax invoke SlotReelPosition3D,reel cmp eax,previous jb slots_test_fail mov previous,eax inc timer cmp timer,3000 jbe slots_test_monotonic_tick inc reel cmp reel,3 jb slots_test_monotonic_reel ; Force known stops for visual timing snapshots; neither drawing nor a panel pays. mov slotsTestStage3D,5 mov slotSpinState,SLOT_READY mov slotStops3D,0 mov slotStops3D[4],6 mov slotStops3D[8],12 mov bankroll,100000 call StartNativeSlots3D mov slotReel1,0 mov slotReel2,0 mov slotReel3,0 xor esi,esi slots_test_force_sevens: mov slotTargets3D[esi*4],10 mov eax,10 sub eax,slotStartStops3D[esi*4] jns @F add eax,20 @@: add eax,slotCycles3D[esi*4] shl eax,6 mov slotTravel3D[esi*4],eax inc esi cmp esi,3 jb slots_test_force_sevens xor esi,esi slots_test_snapshots: mov eax,slotsTestTimes[esi*4] mov slotSpinTimer,eax mov eax,rngSeed3D mov rngSnapshot,eax call RenderNativeSlots3D mov eax,rngSnapshot cmp rngSeed3D,eax jne slots_test_fail cmp bankroll,99900 jne slots_test_fail mov eax,slotsTestTimes[esi*4] cmp slotSpinTimer,eax jne slots_test_fail invoke WriteActionSnapshot3D,slotsTestPaths[esi*4] test eax,eax jz slots_test_fail inc esi cmp esi,4 jb slots_test_snapshots call SettleNativeSlots3D cmp bankroll,109900 jne slots_test_fail call RenderNativeSlots3D invoke WriteActionSnapshot3D,OFFSET slotsTest5 mov pressedKeys3D[50h],1 mov deltaTime3D,16 call HandleNativeSlots3D cmp slotPaytable3D,1 jne slots_test_fail call RenderNativeSlots3D invoke WriteActionSnapshot3D,OFFSET slotsTest6 call StartNativeSlots3D cmp bankroll,109900 jne slots_test_fail call SlotEscape3D cmp slotPaytable3D,0 jne slots_test_fail cmp engineMode,MODE_SLOTS jne slots_test_fail ; Header clicks use the same safe queued-exit path as Escape. mov slotsTestStage3D,6 call StartNativeSlots3D invoke SlotMultiplier3D,slotReel1,slotReel2,slotReel3 imul eax,100 add eax,109800 mov expected,eax mov mouseClick3D,1 mov mouseX3D,280 mov mouseY3D,9 call HandleNativeSlots3D cmp slotLeaveAfter3D,1 jne slots_test_fail cmp paused3D,0 jne slots_test_fail cmp engineMode,MODE_SLOTS jne slots_test_fail call RenderNativeSlots3D invoke WriteActionSnapshot3D,OFFSET slotsTest7 mov deltaTime3D,250 mov ticks,0 slots_test_exit_tick: call HandleNativeSlots3D inc ticks cmp ticks,13 ja slots_test_fail cmp engineMode,MODE_SLOTS je slots_test_exit_tick cmp engineMode,MODE_WALK jne slots_test_fail mov eax,expected cmp bankroll,eax jne slots_test_fail mov eax,oldX cmp playerX,eax jne slots_test_fail mov eax,oldY cmp playerY,eax jne slots_test_fail mov eax,oldAngle cmp playerAngle,eax jne slots_test_fail ; Mouse and key edge controls have identical bounded wager selections. mov slotsTestStage3D,7 mov engineMode,MODE_SLOTS call InitNativeSlots3D test eax,eax jz slots_test_fail mov mouseX3D,44 mov mouseY3D,226 mov mouseClick3D,1 call HandleNativeSlots3D cmp slotBet,100 jne slots_test_fail call HandleNativeSlots3D cmp slotBet,100 jne slots_test_fail mov pressedKeys3D[53h],1 call HandleNativeSlots3D cmp slotBet,50 jne slots_test_fail call HandleNativeSlots3D cmp slotBet,50 jne slots_test_fail mov slotBetIndex3D,0 mov slotBet,10 mov pressedKeys3D[28h],1 call HandleNativeSlots3D cmp slotBet,10 jne slots_test_fail mov slotBetIndex3D,6 mov slotBet,10000 mov pressedKeys3D[26h],1 call HandleNativeSlots3D cmp slotBet,10000 jne slots_test_fail mov bankroll,100000 call StartNativeSlots3D invoke SlotMultiplier3D,slotReel1,slotReel2,slotReel3 imul eax,10000 add eax,90000 mov expected,eax mov slotSpinTimer,2999 mov deltaTime3D,16 mov mouseX3D,280 mov mouseY3D,9 mov mouseClick3D,1 call HandleNativeSlots3D cmp engineMode,MODE_WALK jne slots_test_fail mov eax,expected cmp bankroll,eax jne slots_test_fail mov engineMode,MODE_SLOTS call CardTableEscape3D cmp eax,1 jne slots_test_fail cmp engineMode,MODE_WALK jne slots_test_fail ; Bank cap and idempotent settlement remain safe at the maximum wager. mov bankroll,999999999 mov slotStake3D,10000 mov slotReel1,7 mov slotReel2,7 mov slotReel3,7 mov slotSpinState,SLOT_SPIN call SettleNativeSlots3D cmp bankroll,1000000000 jne slots_test_fail call SettleNativeSlots3D cmp bankroll,1000000000 jne slots_test_fail mov eax,oldBank mov bankroll,eax mov eax,oldSeed mov rngSeed3D,eax mov eax,oldMode mov engineMode,eax call ClearKeys3D call FreeNativeSlots3D xor eax,eax ret slots_test_fail: mov eax,slotsTestStage3D add eax,440 ret RunSlotsTest3D ENDP