solitaire-c64/carddeck.c65

289 lines
5.4 KiB
Text

#IFNDEF __lib_carddeck
#DEFINE __lib_carddeck 1
#INCLUDE "random.c65"
GOTO __skip_lib_carddeck
// Validation error codes
BYTE CONST VALIDATE_OK = 0
BYTE CONST VALIDATE_DUPLICATE = 1
BYTE CONST VALIDATE_MISSING = 2
BYTE CONST VALIDATE_INVALID = 3
// Temporary array to track seen cards (52 bytes)
LABEL validate_seen
ASM
!fill 52, 0
ENDASM
// Initialize stock with all 52 cards (face down)
// Cards stored as $80-$B3 (card value OR'd with CARD_FACEDOWN)
FUNC stock_init
WORD ptr @ $fa
POINTER ptr -> pile_stock
BYTE card
// Set count to 52
POKE ptr[0] , 52
// Fill with cards 0-51, all face down ($80 = CARD_FACEDOWN)
ptr++
FOR card = $80 TO $80+51
POKE ptr , card
ptr++
NEXT
FEND
// Shuffle stock pile using Fisher-Yates algorithm
FUNC stock_shuffle
WORD ptr @ $fa
POINTER ptr -> pile_stock
BYTE i
BYTE j
BYTE temp_a
BYTE temp_b
// Fisher-Yates: swap each card with a random card from remaining deck
i = 52
WHILE i >= 2
rand_max(i, j)
j++
// Swap cards at positions i and j (1-indexed in pile)
IF i <> j
temp_a = PEEK ptr[i]
temp_b = PEEK ptr[j]
POKE ptr[i] , temp_b
POKE ptr[j] , temp_a
ENDIF
i--
WEND
FEND
// Deal cards from stock to a single tableau pile
// num_cards: how many cards to deal
// Top card is flipped face up
FUNC deal_to_tableau({WORD tab_ptr @ $fc} {BYTE num_cards})
WORD stock_ptr @ $fa
POINTER stock_ptr -> pile_stock
BYTE stock_count
BYTE card
BYTE i
// Set tableau count
POKE tab_ptr[0] , num_cards
// Deal cards from stock to tableau
stock_count = PEEK stock_ptr[0]
FOR i = 1 TO num_cards
card = PEEK stock_ptr[stock_count]
POKE tab_ptr[i] , card
stock_count--
NEXT
// Update stock count
POKE stock_ptr[0] , stock_count
// Flip top card face up (clear CARD_FACEDOWN bit)
card = PEEK tab_ptr[num_cards]
card = card & CARD_MASK
POKE tab_ptr[num_cards] , card
FEND
// Deal cards to all 7 tableau piles (Klondike layout)
// Tab0=1, Tab1=2, Tab2=3, Tab3=4, Tab4=5, Tab5=6, Tab6=7 cards
// Top card of each tableau is face up
FUNC deal_tableaus
WORD ptr @ $fc
POINTER ptr -> pile_tab0
deal_to_tableau(ptr, 1)
POINTER ptr -> pile_tab1
deal_to_tableau(ptr, 2)
POINTER ptr -> pile_tab2
deal_to_tableau(ptr, 3)
POINTER ptr -> pile_tab3
deal_to_tableau(ptr, 4)
POINTER ptr -> pile_tab4
deal_to_tableau(ptr, 5)
POINTER ptr -> pile_tab5
deal_to_tableau(ptr, 6)
POINTER ptr -> pile_tab6
deal_to_tableau(ptr, 7)
FEND
// Clear the seen array
FUNC validate_clear_seen
WORD ptr @ $fa
POINTER ptr -> validate_seen
BYTE i
FOR i = 0 TO 51
POKE ptr[i] , 0
NEXT
FEND
// Check cards in a pile, mark as seen
// Returns 0 if OK, 1 if duplicate found
FUNC validate_pile({WORD pile_ptr @ $fa} out:{BYTE result})
WORD seen_ptr @ $fc
POINTER seen_ptr -> validate_seen
BYTE count
BYTE i
BYTE card
BYTE card_val
BYTE already_seen
result = VALIDATE_OK
count = PEEK pile_ptr[0]
FOR i = 1 TO count
card = PEEK pile_ptr[i]
card_val = card & CARD_MASK
// Check if valid card (0-51)
IF card_val >= 52
result = VALIDATE_INVALID
EXIT
ENDIF
already_seen = PEEK seen_ptr[card_val]
IF already_seen
result = VALIDATE_DUPLICATE
EXIT
ENDIF
POKE seen_ptr[card_val] , 1
NEXT
FEND
// Validate entire deck across all piles
// Returns: 0=OK, 1=duplicate, 2=missing
FUNC validate_deck(out:{BYTE result})
WORD ptr @ $fa
WORD seen_ptr @ $fc
BYTE pile_result
BYTE i
BYTE seen
// Clear seen array
validate_clear_seen()
result = VALIDATE_OK
// Check stock
POINTER ptr -> pile_stock
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
// Check waste
POINTER ptr -> pile_waste
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
// Check tableau piles
POINTER ptr -> pile_tab0
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_tab1
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_tab2
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_tab3
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_tab4
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_tab5
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_tab6
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
// Check foundation piles
POINTER ptr -> pile_found0
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_found1
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_found2
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
POINTER ptr -> pile_found3
validate_pile(ptr, pile_result)
IF pile_result
result = pile_result
EXIT
ENDIF
// Now check all 52 cards were seen
POINTER seen_ptr -> validate_seen
FOR i = 0 TO 51
seen = PEEK seen_ptr[i]
IF seen == 0
result = VALIDATE_MISSING
EXIT
ENDIF
NEXT
FEND
LABEL __skip_lib_carddeck
#IFEND