HiddenSinglesColumnPass

Purpose: Scan each column. For each digit 1–9, determine whether that digit can legally appear in exactly one unsolved cell within the column. If so, write the digit into that cell.

Interface

Input:

• Board base address known globally• Uses BuildCandidateMask (returns BC mask)• Uses SingleCandidateFromMask

Output:

• Writes solved digits directly into board• Sets ProgressFlag if at least one cell filled• If contradiction detected (mask == 0 for unsolved cell), jumps to SolverContradiction

Register Conventions

• IX = column base pointer• IY = cell pointer during scan• D = digit (1–9)• E = row counter• BC = candidate mask• HL = working pointer• A = scratch

Implementation

HiddenSinglesColumnPass:

    LD   IX,Board        ; IX = base of board
    LD   C,9             ; column counter

ColumnLoop:

    PUSH BC              ; preserve column counter

    LD   D,1             ; digit = 1

DigitLoop:

    LD   E,9             ; 9 rows per column
    LD   HL,0            ; HL = bitmask accumulator of candidate locations
    LD   IY,IX           ; IY = first cell in column

RowScanLoop:

    LD   A,(IY)
    OR   A
    JR   NZ,NextRow      ; skip solved cells

    PUSH DE
    PUSH HL
    PUSH IY

    CALL BuildCandidateMask   ; BC = candidate mask

    LD   A,B
    OR   C
    JP   Z,SolverContradiction

    ; Test if digit D is allowed in this cell
    LD   A,D
    DEC  A               ; convert 1–9 to bit 0–8

    LD   B,0
    LD   C,1
ShiftLoop:
    OR   A
    JR   Z,ShiftDone
    SLA  C
    RL   B
    DEC  A
    JR   ShiftLoop
ShiftDone:

    LD   A,B
    AND  H
    LD   A,C
    AND  L
    JR   NZ,NotCandidate

    ; Add this row position to accumulator mask
    LD   A,E
    DEC  A               ; row index 0–8

    PUSH BC
    LD   B,0
    LD   C,1
PosShift:
    OR   A
    JR   Z,PosDone
    SLA  C
    RL   B
    DEC  A
    JR   PosShift
PosDone:

    LD   A,H
    OR   B
    LD   H,A
    LD   A,L
    OR   C
    LD   L,A
    POP  BC

NotCandidate:

    POP  IY
    POP  HL
    POP  DE

NextRow:

    LD   BC,9
    ADD  IY,BC           ; move down one row (row stride = 9)

    DEC  E
    JR   NZ,RowScanLoop

    ; Check accumulator HL for exactly one bit
    LD   B,H
    LD   C,L
    CALL SingleCandidateFromMask
    JR   Z,NoWrite

    ; A = row index+1 of single position
    DEC  A               ; convert to 0–8

    LD   IY,IX           ; reset to top of column

WriteSeek:
    OR   A
    JR   Z,WriteHere
    LD   BC,9
    ADD  IY,BC
    DEC  A
    JR   WriteSeek

WriteHere:
    LD   A,D
    LD   (IY),A          ; write digit

    LD   A,1
    LD   (ProgressFlag),A

NoWrite:

    INC  D
    LD   A,D
    CP   10
    JR   NZ,DigitLoop

    POP  BC              ; restore column counter

    INC  IX              ; move to next column base

    DEC  C
    JR   NZ,ColumnLoop

    RET