|
8 | 8 | const RIFT_STATUS_DELAY_MS = 950; |
9 | 9 | const RIFT_STATUS_DELAY_REDUCED_MOTION_MS = 500; |
10 | 10 | const RIFT_EVALUATE_DEBOUNCE_MS = 140; |
| 11 | + const RIFT_NON_CONFLICT_CHECK_INTERVAL = 5; |
11 | 12 |
|
12 | 13 | const boardEl = document.getElementById('board'); |
13 | 14 | const statusEl = document.getElementById('status'); |
|
45 | 46 | let statusSequenceId = 0; |
46 | 47 | let interactionLocked = false; |
47 | 48 | let riftEvalTimer = null; |
| 49 | + let movesSinceSolvabilityCheck = 0; |
48 | 50 | let riftState = { |
49 | 51 | active:false, |
50 | 52 | sequenceRunning:false, |
|
296 | 298 | riftState.active=false; |
297 | 299 | riftState.sequenceRunning=false; |
298 | 300 | riftState.nodes=[]; |
| 301 | + movesSinceSolvabilityCheck=0; |
299 | 302 | boardShellEl.classList.remove('rift-active','rift-glitch'); |
300 | 303 | statusEl.classList.remove('rift-status'); |
301 | 304 | riftModal.hidden=true; |
|
362 | 365 | saveGame(); |
363 | 366 | } |
364 | 367 |
|
365 | | - function evaluateRiftTrigger(origin='system'){ |
| 368 | + function evaluateRiftTrigger(origin='system', options={}){ |
366 | 369 | if(origin!=='player-move') return; |
367 | 370 | if(riftState.active||riftState.sequenceRunning) return; |
368 | 371 | if(Date.now()<riftState.cooldownUntil) return; |
| 372 | + if(!options.force){ |
| 373 | + if(options.conflictIntroduced){ |
| 374 | + movesSinceSolvabilityCheck=0; |
| 375 | + } else { |
| 376 | + movesSinceSolvabilityCheck++; |
| 377 | + if(movesSinceSolvabilityCheck<RIFT_NON_CONFLICT_CHECK_INTERVAL) return; |
| 378 | + movesSinceSolvabilityCheck=0; |
| 379 | + } |
| 380 | + } |
369 | 381 | const solvable=hasAnySolution(grid); |
370 | 382 | if(solvable){ |
371 | 383 | boardWasSolvable=true; |
|
378 | 390 | } |
379 | 391 | } |
380 | 392 |
|
381 | | - function scheduleRiftEvaluation(origin='player-move'){ |
| 393 | + function scheduleRiftEvaluation(origin='player-move', options={}){ |
382 | 394 | if(riftEvalTimer) clearTimeout(riftEvalTimer); |
383 | 395 | riftEvalTimer=setTimeout(()=>{ |
384 | 396 | riftEvalTimer=null; |
385 | | - evaluateRiftTrigger(origin); |
| 397 | + evaluateRiftTrigger(origin, options); |
386 | 398 | },RIFT_EVALUATE_DEBOUNCE_MS); |
387 | 399 | } |
388 | 400 |
|
|
663 | 675 | boardShellEl.classList.remove('victory-glow'); |
664 | 676 | clearRiftVisualState(); |
665 | 677 | riftState={active:false,sequenceRunning:false,nodes:[],hasTriggered:false,cooldownUntil:0,copyKey:'pattern'}; |
| 678 | + movesSinceSolvabilityCheck=0; |
666 | 679 | captureLastSolvableSnapshot(); |
667 | 680 | render(); startTimer(); saveGame(); hideSplash(); |
668 | 681 | setStatus(`New ${difficultyEl.value} Shandoku game loaded.`); |
|
683 | 696 | grid[r][c]=n; |
684 | 697 | notes[r][c].clear(); |
685 | 698 | autoCleanNotesAround(r,c,n); |
| 699 | + const introducedConflict=hasConflict(r,c); |
686 | 700 | render(); saveGame(); |
687 | | - if(hasConflict(r,c)){ |
| 701 | + if(introducedConflict){ |
688 | 702 | vibrate([10,50,10]); |
689 | 703 | const cellEl=boardEl.querySelector(`[data-r="${r}"][data-c="${c}"]`); |
690 | 704 | if(cellEl){ cellEl.classList.add('error-shake'); setTimeout(()=>cellEl.classList.remove('error-shake'),400); } |
691 | 705 | } else { |
692 | 706 | vibrate(10); |
693 | 707 | } |
694 | 708 | if(isSolved()){ setStatus('Solved. Nice work.'); celebrate(); } |
695 | | - scheduleRiftEvaluation('player-move'); |
| 709 | + scheduleRiftEvaluation('player-move',{conflictIntroduced:introducedConflict}); |
696 | 710 | } |
697 | 711 |
|
698 | 712 | function clearSelected(){ |
|
704 | 718 | grid[r][c]=0; notes[r][c].clear(); |
705 | 719 | render(); saveGame(); |
706 | 720 | setStatus('Cell cleared.'); |
707 | | - scheduleRiftEvaluation('player-move'); |
| 721 | + scheduleRiftEvaluation('player-move',{conflictIntroduced:false}); |
708 | 722 | } |
709 | 723 |
|
710 | 724 | function jumpToNextEmpty(){ |
|
0 commit comments