eris2010

Documentation: http://frombelow.net/projects/eris2010/
Clone: git clone https://git.frombelow.net/eris2010.git
Log | Files | Refs | Submodules | README | LICENSE

board.asm (14168B)


      1 ;;; Copyright 2021 Gerd Beuster (gerd@frombelow.net). This is free
      2 ;;; software under the GNU GPL v3 license or any later version. See
      3 ;;; COPYING in the root directory for details.
      4 
      5 ;;; This file contains the definitions and subroutines
      6 ;;; related to the game boards, like printing the
      7 ;;; game board and placing pieces. It also contains
      8 ;;; the subroutines to check for a win constallation
      9 ;;; on the board, and for playing the game.
     10 
     11 ;;; Orchester a game between two players
     12 ;;; Each player has an init subroutine, called
     13 ;;; once at the beginning, and a ply subroutine,
     14 ;;; called on every turn of the player.
     15 ;;; player_x_init_ptr - Init function of player X
     16 ;;; player_x_ply_ptr  - Ply function of player X
     17 ;;; player_o_init_ptr - Init function of player O
     18 ;;; player_o_ply_ptr  - Ply function of player O
     19 play_game:
     20         .block
     21         jsr init_board
     22         ;; jsr does not allow indirect addressing,
     23         ;; therefore we have to wrap the indirect
     24         ;; calls this way.
     25         jsr player_x_init
     26         jsr player_o_init
     27 play_game_loop:   
     28         lda #piece_x
     29         jsr next_ply
     30         bne game_finished
     31         lda #piece_o
     32         jsr next_ply
     33         bne game_finished
     34         jmp play_game_loop
     35 game_finished:
     36         pha
     37         jsr print_board
     38         pla
     39         jsr print_game_state
     40         jsr io.putnl
     41         rts
     42 next_ply:
     43         cmp #piece_x
     44         bne ply_player_o
     45         ;; Player X's ply
     46         ;; jsr does not allow indirect addressing,
     47         ;; threfore we have to wrap the indirect
     48         ;; calls this way.
     49         jsr player_x_ply
     50         jmp cont
     51 ply_player_o:   
     52         jsr player_o_ply
     53 cont:   
     54         jsr get_game_state
     55         cmp #$ff
     56         rts
     57 player_x_init:  
     58         jmp (player_x_init_ptr)
     59 player_o_init:  
     60         jmp (player_o_init_ptr)
     61 player_x_ply:  
     62         jmp (player_x_ply_ptr)
     63 player_o_ply:  
     64 before_o:       
     65         jmp (player_o_ply_ptr)
     66 after_o:        
     67         .bend
     68         
     69 ;;; We store the Tic-Tac-Toe board in three
     70 ;;; bytes, one for each row. When passing
     71 ;;; a board on the stack, the first row is
     72 ;;; pushed first, followed by the second,
     73 ;;; and third.
     74 ;;; 
     75 ;;; Each field in a row is represented by
     76 ;;; two bits:
     77 ;;; %00 - Field unoccupied
     78 ;;; %01 - Field occupied by player "X"
     79 ;;; %10 - Field occupied by player "O"
     80 
     81 piece_none = %00
     82 piece_x = %01
     83 piece_o = %10
     84 ;;; 
     85 ;;; In the byte for a row, the first
     86 ;;; two bits represent the leftmost field.
     87 ;;; The last two bits are not used.
     88 ;;; 
     89 ;;; A contains the piece
     90 ;;; X contains the position (0-8)
     91 ;;; The location of the board is read
     92 ;;; as a 16 bit address from address
     93 ;;; board.
     94 
     95 
     96 ;;; Initialize and clear the board.
     97 ;;; When init_board is called, the
     98 ;;; "standard" board location as given
     99 ;;; in variable board is transferred
    100 ;;; to board_ptr before clearing the
    101 ;;; board.
    102 ;;; If clear_board is called, the
    103 ;;; board at board_ptr is cleared
    104 ;;; without changing board_ptr.
    105 init_board:
    106         lda #<main_board
    107         sta board_ptr
    108         lda #>main_board
    109         sta board_ptr+1
    110 clear_board:
    111         ;; Clear board
    112         lda #$00
    113         ldy #$00
    114         sta (board_ptr),y
    115         iny
    116         sta (board_ptr),y
    117         iny
    118         sta (board_ptr),y
    119         rts
    120         
    121 mirror_board:
    122         .block
    123         ;; Mirror board on the diagonal axis
    124         ;; such that we can
    125         ;; use the win_row subroutine.
    126         ;; First row to column
    127         ldy #$00
    128         lda (board_ptr),y
    129         and #%00000011
    130         sta (other_board_ptr),y
    131         lda (board_ptr),y
    132         and #%00001100
    133         lsr a
    134         lsr a
    135         ldy #$01
    136         sta (other_board_ptr),y
    137         ldy #$00
    138         lda (board_ptr),y
    139         and #%00110000
    140         lsr a
    141         lsr a
    142         lsr a
    143         lsr a
    144         ldy #$02
    145         sta (other_board_ptr),y
    146         ;; Second row to column
    147         ldy #$01
    148         lda (board_ptr),y
    149         and #%00000011
    150         asl a
    151         asl a
    152         ldy #$00
    153         ora (other_board_ptr),y
    154         sta (other_board_ptr),y
    155         ldy #$01
    156         lda (board_ptr),y
    157         and #%00001100
    158         ora (other_board_ptr),y
    159         sta (other_board_ptr),y
    160         ldy #$01
    161         lda (board_ptr),y
    162         and #%00110000
    163         lsr a
    164         lsr a
    165         ldy #$02
    166         ora (other_board_ptr),y
    167         sta (other_board_ptr),y
    168         ;; Third column to row
    169         lda (board_ptr),y
    170         and #%00000011
    171         asl a
    172         asl a
    173         asl a
    174         asl a
    175         ldy #$00
    176         ora (other_board_ptr),y
    177         sta (other_board_ptr),y
    178         ldy #$02
    179         lda (board_ptr),y
    180         and #%00001100
    181         asl a
    182         asl a
    183         ldy #$01
    184         ora (other_board_ptr),y
    185         sta (other_board_ptr),y
    186         ldy #$02
    187         lda (board_ptr),y
    188         and #%00110000
    189         ora (other_board_ptr),y
    190         sta (other_board_ptr),y
    191         rts
    192         .bend
    193         
    194 ;;; Set a field on the board.
    195 ;;; A is the field, X the position.
    196 ;;; This subroutine changes A, X, and Y.
    197 set_field:
    198         ;; Convert position on X
    199         ;; to column/row in X/Y.
    200         jsr pos_to_column_row
    201         ;; Set field based on X/Y coordinates
    202 set_field_x_y:
    203         .block
    204         cpx #$00
    205         beq change_board
    206         asl a
    207         asl a
    208         dex
    209         jmp set_field_x_y
    210 change_board:
    211         ora (board_ptr),y
    212         sta (board_ptr),y
    213         rts
    214         .bend
    215 
    216 ;;; Get value of field on the board.
    217 ;;; X is the position. Field value is
    218 ;;; returned in A.
    219 get_field:
    220         .block
    221         ;; Convert position on X
    222         ;; to column/row in X/Y.
    223         jsr pos_to_column_row
    224         lda (board_ptr),y
    225 field_loop:
    226         cpx #$00
    227         beq got_field
    228         lsr a
    229         lsr a
    230         dex
    231         jmp field_loop
    232 got_field:
    233         and #%11
    234         rts
    235         .bend
    236         
    237 ;;; Convert position in X
    238 ;;; register to row/column
    239 ;;; coordinates.
    240 ;;; X becomes the column,
    241 ;;; Y the row
    242 pos_to_column_row:
    243         .block
    244         ldy #$00
    245 pos_loop:  
    246         cpx #$03
    247         bcc pos_found
    248         iny
    249         dex
    250         dex
    251         dex
    252         jmp pos_loop
    253 pos_found:
    254         rts
    255         .bend
    256 
    257 
    258 copy_field:
    259         ;; Copy field X from board_ptr
    260         ;; to field Y of other_board_ptr
    261         ;; Save variables
    262         lda board_ptr
    263         pha
    264         lda board_ptr+1
    265         pha
    266         phy
    267         ;; Get value & save ot
    268         jsr get_field
    269         pha
    270         ;; Write value
    271         lda other_board_ptr
    272         sta board_ptr
    273         lda other_board_ptr+1
    274         sta board_ptr+1
    275         pla
    276         plx
    277         jsr set_field
    278         ;; Restore variables
    279         pla
    280         sta board_ptr+1
    281         pla
    282         sta board_ptr
    283         rts
    284 
    285 COPY_FIELD	.macro
    286         ldx #\1
    287         ldy #\2
    288         jsr copy_field
    289 	.endm
    290         
    291 SET_FIELD	.macro
    292         lda #\1
    293         ldx #\2
    294         jsr set_field
    295 	.endm
    296 
    297 GET_FIELD	.macro
    298         #mem.STORE_WORD \1, board_ptr
    299         ldx #\2
    300         jsr get_field
    301 	.endm
    302 
    303 ;;; Print Tic-Tac-Toe board.
    304 ;;; Note that the printed
    305 ;;; numbers start with 1, while
    306 ;;; the internal counting of
    307 ;;; fields starts with 0.
    308 print_board:
    309         .block
    310         ldx #$ff
    311         phx
    312 	jsr term.clear_screen
    313 	#term.SET_CURSOR #$01, #$01
    314         #io.PRINTSNL '** Tic-Tac-Toe **'
    315         #io.PRINTNL
    316         #io.PRINTSNL '+---+---+---+'
    317 print_field:
    318         lda #$03
    319         sta tmp                 ; Print each line 3 times
    320 print_line:     
    321         lda #'|'
    322         jsr io.putc
    323         plx
    324         inx
    325         phx
    326         jsr get_field
    327         jsr piece_to_ascii
    328         jsr io.putc
    329         plx
    330         phx
    331         jsr put_field_number_if_empty_and_middle
    332         jsr io.putc
    333         plx
    334         phx
    335         cpx #$02
    336         beq print_row_seperator
    337         cpx #$05
    338         beq print_row_seperator
    339         cpx #$08
    340         beq print_row_seperator
    341 check_if_all_rows_printed:
    342         plx
    343         phx
    344         cpx #$08
    345         bne print_line
    346         plx                     ; Clean-up stack
    347         rts
    348 print_row_seperator:
    349         #io.PRINTSNL '|'
    350         dec tmp                 ; Check if each line has been printed 3 times
    351         lda tmp
    352         beq print_seperator
    353         pla                     ; If not,
    354         sec                     ; print
    355         sbc #$03                ; line
    356         pha
    357         jmp print_line          ;again.
    358 print_seperator:        
    359         #io.PRINTSNL '+---+---+---+'
    360         lda #$03
    361         sta tmp
    362         jmp check_if_all_rows_printed
    363 put_field_number_if_empty_and_middle:
    364         pha
    365         cmp #' '
    366         bne print_verbatim
    367         ;; Check if we are in the
    368         ;; middle line
    369         lda tmp
    370         cmp #$02
    371         bne print_verbatim
    372         ;; Middle of empty field;
    373         ;; print field number
    374         txa
    375         clc
    376         adc #'1'
    377         jsr io.putc
    378         pla
    379         rts
    380 print_verbatim:
    381         pla
    382         jmp io.putc
    383         .bend
    384 
    385 
    386         
    387 piece_to_ascii:
    388         .block
    389         cmp #piece_x
    390         bne not_x
    391         lda #'#'
    392         rts
    393 not_x:  
    394         cmp #piece_o
    395         bne not_o
    396         lda #':'
    397         rts
    398 not_o:  
    399         lda #' '
    400         rts
    401         .bend
    402         
    403 ;;; Check if a player won.
    404 ;;; Returns piece_x or piece_y
    405 ;;; if one of the player won,
    406 ;;; piece_none in case of a draw,
    407 ;;; and #$ff in case the game
    408 ;;; is not finished yet.
    409         piece_x_row = (piece_x << 4) + (piece_x << 2) + piece_x
    410         piece_o_row = (piece_o << 4) + (piece_o << 2) + piece_o
    411 get_game_state:
    412         .block
    413         lda #piece_x_row
    414         jsr check_player_won
    415         bne done
    416         lda #piece_o_row
    417         jsr check_player_won
    418         bne done
    419         jsr check_draw
    420 done:   
    421         rts
    422         .bend
    423 
    424 ;;; Check if a certain player one.
    425 ;;; This subroutine expects a complete
    426 ;;; row of pieces of the player to be
    427 ;;; checked for winning in A as input.
    428 check_player_won:
    429         .block
    430         sta tmp                 ; Store pattern for following checks
    431         jsr check_win_row
    432         bne done
    433         lda tmp
    434         jsr check_win_column
    435         bne done
    436         lda tmp
    437         jsr check_win_first_diagonal
    438         bne done
    439         lda tmp
    440         jsr check_win_second_diagonal
    441 done:   
    442         rts
    443         .bend
    444 
    445 ;;; Check for three in a row.
    446 ;;; Expects the row pattern of
    447 ;;; three identical pieces in A.
    448 check_win_row:
    449         .block
    450         ldy #$00
    451 loop:   
    452         cmp (board_ptr),y
    453         beq player_won
    454         iny
    455         cpy #$03
    456         bne loop
    457         ;; No winning rows
    458         lda #$00
    459         rts
    460 player_won:
    461         lda tmp
    462         and #%00000011
    463         rts
    464         .bend
    465 
    466 ;;; Check for three in a column.
    467 ;;; Expects the row(!) pattern of
    468 ;;; three identical pieces in A.
    469 check_win_column:
    470         .block
    471         and #%00110000          ; Field (column) pattern
    472         tax                     ; We use X as temporary memory
    473         ldy #$00
    474 loop:   
    475         and (board_ptr),y       ; Piece in column?
    476         beq check_next_column   ; If not, check next column
    477         iny                     ; If yes, advance to next row
    478         cpy #$03                ; Until all three rows
    479         bne loop                ; have been checked
    480 player_won:     
    481         lda tmp
    482         and #%00000011
    483         rts
    484 check_next_column:
    485         ldy #$00                ; Restore row counter
    486         txa                     ; Old field (column) pattern
    487         lsr a                     ; Change to
    488         lsr a                     ; next column
    489         tax                     ; and save it in temporary memory
    490         bne loop 
    491         ;; No winning columns
    492         lda #$00
    493         rts
    494         .bend
    495 
    496 check_win_first_diagonal:
    497         .block
    498         and #%00110000          ; Start with last field (column)
    499         tax                     ; Store pattern in x
    500         ldy #$00                ; Row number stored in y
    501 loop:
    502         and (board_ptr),y
    503         beq no_win_first_diagonal
    504         txa                     ; Move
    505         lsr a                     ; field
    506         lsr a                     ; (column)
    507         tax                     ; pattern
    508         iny                     ; and row by one
    509         cpy #$03
    510         bne loop
    511         ;; Player won
    512         ;; Return player piece
    513         lda tmp
    514         and #%00000011
    515         rts
    516 no_win_first_diagonal:
    517         lda #$00
    518         rts
    519         .bend
    520         
    521 check_win_second_diagonal:
    522         .block
    523         and #%00000011          ; Start with first field (column)
    524         tax                     ; Store pattern in x
    525         ldy #$00                ; Row number stored in y
    526 loop:
    527         and (board_ptr),y
    528         beq no_win_second_diagonal
    529         txa                     ; Move
    530         asl a                     ; field
    531         asl a                     ; (column)
    532         tax                     ; pattern
    533         iny                     ; and row by one
    534         cpy #$03
    535         bne loop
    536         ;; Player won
    537         ;; Return player piece
    538         lda tmp
    539         and #%00000011
    540         rts
    541 no_win_second_diagonal:
    542         lda #$00
    543         rts
    544         .bend
    545         
    546 check_draw:
    547         .block
    548         ldy #$00                ; Row
    549 row_loop:       
    550         ldx #%00110000          ; Field (column) pattern
    551         txa                     ; Store pattern in x
    552 col_loop:       
    553         and (board_ptr),y       ; Check
    554         beq no_draw             ; all
    555         txa                     ; fields
    556         lsr a                     ; for
    557         lsr a                     ; emptiness
    558         tax
    559         bne col_loop
    560         iny
    561         cpy #$03
    562         bne row_loop
    563         ;; Draw
    564         lda #piece_none
    565         rts
    566 no_draw:
    567         lda #$ff
    568         rts
    569         .bend      
    570         
    571 ;;; This subroutine can be called
    572 ;;; called after get_game_state
    573 ;;; in order to print the state
    574 ;;; of the game in human-friendly
    575 ;;; format to the acai.
    576 print_game_state:
    577         .block
    578         cmp #piece_x
    579         beq somebody_won
    580         cmp #piece_o
    581         beq somebody_won
    582         cmp #piece_none
    583         bne no_draw
    584         #io.PRINTSNL "Draw!"
    585         rts
    586 no_draw:        
    587         #io.PRINTSNL "Let the game continue!"
    588         rts
    589 somebody_won:
    590         pha
    591         #io.PRINTS "Player "
    592         pla
    593         clc
    594         adc #'0'
    595         jsr io.putc
    596         #io.PRINTSNL " wins!"
    597         rts
    598         .bend