million-perfect-letters/src/puzzle.a
2020-05-12 15:53:25 -04:00

288 lines
7.7 KiB
Plaintext

;license:MIT
;(c) 2020 by 4am
;
; data storage routines for puzzles
;
; Public functions:
; - InitPuzzleStorage
; - AddLineToPuzzle
; - AddTargetWordToPuzzle
; - CheckForTargetWord
; - FindLetterInColumn
; - ScrollPuzzleDown
; - ScrollPuzzleUp
; - IsPuzzleComplete
puzzle_logical_width = $0300 ; [0x01 byte ] number of letters per word (4..7)
puzzle_logical_height = $0301 ; [0x01 byte ] number of rows with letters (1..5)
puzzle_offsets = $0302 ; [0x08 bytes] how far each column has been scrolled down from top of grid
; 9 rows of data for current puzzle
; each byte is one of
; - letter with high bit off (unmatched letter)
; - letter with high bit on (matched letter, displayed in grey)
; - 0x00 (unused space)
; if letters per word is less than 7, extra bytes at end of each row are guaranteed to be 0x00
puzzle_data0 = $030A ; [0x08 bytes] character data
puzzle_data1 = $0312 ; [0x08 bytes]
puzzle_data2 = $031A ; [0x08 bytes]
puzzle_data3 = $0322 ; [0x08 bytes]
puzzle_data4 = $032A ; [0x08 bytes]
puzzle_data5 = $0332 ; [0x08 bytes]
puzzle_data6 = $033A ; [0x08 bytes]
puzzle_data7 = $0342 ; [0x08 bytes]
puzzle_data8 = $034A ; [0x08 bytes]
puzzle_word_count = $0352 ; [0x01 byte ] number of records in <puzzle_words>
puzzle_words = $0353 ; [0x80 bytes]
; each 8-byte record is a <puzzle_logical_width>-length word, no prefix
; extra bytes in each record are ignored
InitPuzzleStorage
; in: A = logical puzzle width (number of letters in each word, 4..7)
sta puzzle_logical_width
ldx #$CF
lda #0
- sta puzzle_logical_width, x
dex
bne -
rts
AddLineToPuzzle
; in: ($FE) points to <puzzle_logical_width> length character buffer (no prefix, no suffix)
; out: clobbers all
lda puzzle_logical_height
asl
asl
asl
tax
ldy #0
- lda ($FE), y
cmp #$20
bne +
lda #0
+ sta puzzle_data0, x
inx
iny
cpy puzzle_logical_width
bne -
inc puzzle_logical_height
rts
AddTargetWordToPuzzle
; in: ($FE) points to <puzzle_logical_width> length character buffer (no prefix, no suffix)
; out: clobbers all
lda puzzle_word_count
asl
asl
asl
tax
ldy #0
- lda ($FE), y
sta puzzle_words, x
inx
iny
cpy puzzle_logical_width
bne -
inc puzzle_word_count
rts
unused_counter=$FD
CheckForTargetWord
; in: none
; out: C clear if a target word was found on row 4
; C set if no target word found
; note: will return C=set if target word was found but all letters were
; already used (i.e. word had already been found before but player
; shifted letters out and back)
+LDADDR puzzle_words
+ST16 $FE
ldx #0 ; word index
@checkword
ldy #0 ; character index
- lda puzzle_data4, y
and #$7F ; letters within puzzle have high bit set if they've been used before
cmp ($FE), y ; (which is fine, and on later puzzles is guaranteed to happen eventually)
bne @nextword
iny
cpy puzzle_logical_width
bne -
; found matching word
; set high bit on all letters in row 4
ldy #0
sty unused_counter ; to see if all letters were already used (i.e. this word was already found)
- lda puzzle_data4, y
bmi +
inc unused_counter
ora #$80
sta puzzle_data4, y
+ iny
cpy puzzle_logical_width
bne -
lda unused_counter
beq @notfound
clc
rts
@nextword
lda $FE
clc
adc #8
sta $FE
bcc +
inc $FF
+ inx
cpx puzzle_word_count
bne @checkword
@notfound
sec
rts
lettertofind = $FD
FindLetterInColumn
; in: A = letter (0x41..0x5A)
; Y = logical column to search
; out: C clear if letter was found in given column, and
; A = #$FF if letter was found below center column
; A = #$01 if letter was found above center column
; A = #$00 if letter was found in center column
; C set if letter was not found
; clobbers A/X
; preserves Y
; clobbers $FD,$FE,$FF
ldx #$01
- cmp puzzle_data0, y
beq @down
cmp puzzle_data1, y
beq @down
cmp puzzle_data2, y
beq @down
cmp puzzle_data3, y
beq @down
cmp puzzle_data4, y
beq @even
cmp puzzle_data5, y
beq @up
cmp puzzle_data6, y
beq @up
cmp puzzle_data7, y
beq @up
cmp puzzle_data8, y
beq @up
ora #$80 ; check for used letters now
dex
beq - ; will only branch back once
sec ; no matches on used or unused letters, so we're done
rts
@down clc
lda #$01
rts
@up clc
lda #$FF
rts
@even clc
lda #$00
rts
ScrollPuzzleDown
; in: Y = logical column to scroll
; out: C clear if puzzle was scrolled down
; C set if puzzle was already as far down as it can go
; preserves X/Y
lda puzzle_data3, y
beq +
ScrollPuzzleDownUnconditionally
lda puzzle_data7, y
sta puzzle_data8, y
lda puzzle_data6, y
sta puzzle_data7, y
lda puzzle_data5, y
sta puzzle_data6, y
lda puzzle_data4, y
sta puzzle_data5, y
lda puzzle_data3, y
sta puzzle_data4, y
lda puzzle_data2, y
sta puzzle_data3, y
lda puzzle_data1, y
sta puzzle_data2, y
lda puzzle_data0, y
sta puzzle_data1, y
lda #0
sta puzzle_data0, y
lda puzzle_offsets, y
clc
adc #1
sta puzzle_offsets, y
clc
rts
+ sec
rts
ScrollPuzzleUp
; in: Y = logical column to scroll
; out: C clear if puzzle was scrolled up
; C set if puzzle was already as far up as it can go
; preserves X/Y
lda puzzle_data5, y
beq +
lda puzzle_data1, y
sta puzzle_data0, y
lda puzzle_data2, y
sta puzzle_data1, y
lda puzzle_data3, y
sta puzzle_data2, y
lda puzzle_data4, y
sta puzzle_data3, y
lda puzzle_data5, y
sta puzzle_data4, y
lda puzzle_data6, y
sta puzzle_data5, y
lda puzzle_data7, y
sta puzzle_data6, y
lda puzzle_data8, y
sta puzzle_data7, y
lda #0
sta puzzle_data8, y
lda puzzle_offsets, y
sec
sbc #1
sta puzzle_offsets, y
clc
rts
+ sec
rts
IsPuzzleComplete
; out: C clear if puzzle is complete (all letters have been matched)
; C set if puzzle is not yet complete
ldx #$47
- lda puzzle_data0, x
beq @keepChecking
bmi @keepChecking
sec
rts
@keepChecking
dex
bpl -
clc
rts