all repos — tictactoe @ 3190ad25cb167f8289b7d10957a37283cd1efc2a

tic-tac-toe game for uxn

major refactoring

- fix bug when game restarts and board doesn't get cleared
- optimize by using immediate opcodes and fallthrough subroutines
- lint with uxnlin
- format with uxnfor
- store x and y coordinates of the cursor in different bytes
(i thought this was a cool memory hack, but since the variables
are located in zero page this doesn't save memory, on the contrary,
it adds unnecessary complexity to manage packing and unpacking
coordinates from nibbles)
- remove loops from check-win
- remove nested comments
- avoid redoing some work when game restarts (restart code
now jumps to code that is necessary to only reset game state and
clear the board)
la-ninpre aaoth@aaoth.xyz
Mon, 28 Aug 2023 13:09:56 +0300
commit

3190ad25cb167f8289b7d10957a37283cd1efc2a

parent

fa168f384ba699d7b0e8a4c49925573115e0c976

1 files changed, 178 insertions(+), 296 deletions(-)

jump to
M tictactoe.taltictactoe.tal

@@ -1,325 +1,207 @@

( tic tac toe ) -( devices: {{{ ) -|00 @System - [ &vector $2 - &wst $1 - &rst $5 - &r $2 - &g $2 - &b $2 - &debug $1 - &state $1 - ] -|20 @Screen - [ &vector $2 - &width $2 - &height $2 - &auto $2 - &x $2 - &y $2 - &addr $2 - &pixel $1 - &sprite $1 - ] -|80 @Controller - [ &vector $2 - &button $1 - &key $1 - ] -( }}} ) +|00 @System &vector $2 &wst $1 &rst $5 &r $2 &g $2 &b $2 &debug $1 &state $1 +|20 @Screen &vector $2 &width $2 &height $2 &auto $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 +|80 @Controller &vector $2 &button $1 &key $1 + +( +@| variables ) |0000 -@game - [ &cursor-pos $1 ( high nibble - x, low nibble - y ) - &current-player $1 ( 01 - x, 02 - o ) - &moves $1 ( 00 - 09 ) - &cells $9 ( 00 - empty, 01 - x, 02 - o ) - ] -|0100 ( -> ) - ( add .theme support ? ) - #1c2f .System/r DEO2 - #1b3a .System/g DEO2 - #1c3a .System/b DEO2 + @game &x $1 &y $1 &current-player $1 &moves $1 &cells $9 - #0050 .Screen/width DEO2 - #0050 .Screen/height DEO2 +( +@| vectors ) - ;on-controller .Controller/vector DEO2 +|0100 - ;init-game JSR2 +@on-reset ( -> ) + ( | add .theme support ? ) + #1cf2 .System/r DEO2 + #1ba3 .System/g DEO2 + #1ca3 .System/b DEO2 + ( | size ) + #0050 .Screen/width DEO2 + #0050 .Screen/height DEO2 + ( | draw board ) + ( ) #0010 #000f #3043 #01 draw-line-at + ( ) #000f #0010 #3043 #02 draw-line-at + ( ) #0010 #0040 #3043 #01 draw-line-at + ( ) #0040 #0010 #3043 #02 draw-line-at + ( | draw cells ) + ( ) #0010 #001f #3043 #01 draw-line-at + ( ) #0010 #002f #3043 #01 draw-line-at + ( ) #0010 #003f #3043 #01 draw-line-at + ( ) #001f #0010 #3043 #02 draw-line-at + ( ) #002f #0010 #3043 #02 draw-line-at + ( ) #003f #0010 #3043 #02 draw-line-at + &restart ( -> ) + ;on-controller .Controller/vector DEO2 + init-game BRK - ( border ) - #0010 #000f #30 #02 #01 ;draw-line-at JSR2 - #000f #0010 #30 #02 #02 ;draw-line-at JSR2 - #0010 #0040 #30 #02 #01 ;draw-line-at JSR2 - #0040 #0010 #30 #02 #02 ;draw-line-at JSR2 - - ( cells ) - #0010 #001f #30 #02 #01 ;draw-line-at JSR2 - #0010 #002f #30 #02 #01 ;draw-line-at JSR2 - #0010 #003f #30 #02 #01 ;draw-line-at JSR2 - #001f #0010 #30 #02 #02 ;draw-line-at JSR2 - #002f #0010 #30 #02 #02 ;draw-line-at JSR2 - #003f #0010 #30 #02 #02 ;draw-line-at JSR2 -BRK +@on-controller ( -> ) + [ LIT2 -game/y -game/x ] LDZ SWP LDZ + ( ) .Controller/button DEI #01 ANDk NIP ?&play + #10 ANDk NIP ?&up + #20 ANDk NIP ?&down + #40 ANDk NIP ?&left + #80 ANDk NIP ?&right + POP .Controller/key DEI #20 EQUk NIP ?&play + POP POP2 BRK + &play ( x y key -> ) + POP + ( x y to addr ) SWPk #03 MUL ADD .game/cells ADD + ( ) LDZk #00 EQU ?&draw + POP POP2 BRK + &draw ( x y addr -> ) + .game/current-player LDZ SWP STZk + POP ROT ROT draw-move-at .game/moves LDZk INC SWP STZk + POP + ( game can't be ended before 5 moves ) #05 LTH ?&end + check-win BRK + &up ( x y key -> ) + clear-cursor coord-dec !&redraw + &down ( x y key -> ) + clear-cursor coord-inc !&redraw + &left ( x y key -> ) + clear-cursor SWP coord-dec SWP !&redraw + &right ( x y key -> ) + clear-cursor SWP coord-inc SWP + ( >> ) + &redraw ( x y -> ) + ( save x y ) SWPk .game/x STZk + NIP INC STZ + .game/current-player LDZ draw-cursor-at + &end ( -> ) + BRK + +( +@|core ) @init-game ( -- ) - #11 .game/cursor-pos STZ - #01 .game/current-player STZ - #00 .game/moves STZ + ( | init variables ) + [ LIT2 01 -game/x ] STZk + INC STZk + INC STZ + ( | clear memory ) + #05 + &while ( -- ) + #01 SUBk NIP DUP ADD .game/moves ADD #0000 ROT STZ2 + #01 SUB DUP ?&while + POP + ( | clear screen ) + #0010 .Screen/x DEO2k + INC2 INC2 DEO2 + [ LIT2 80 -Screen/pixel ] DEO + ( cursor draw ) #0101 #01 + ( >> ) - ( clear the board ) - #09 &while - DUP #01 SUB .game/cells ADD #00 SWP STZ - #01 SUB DUP ,&while JCN - POP - - .game/cursor-pos LDZ #01 ;draw-cursor-at JSR2 -JMP2r - -@on-controller ( -> ) - .game/cursor-pos LDZ - - .Controller/button DEI - #01 ANDk NIP ,&play JCN - #10 ANDk NIP ,&up JCN - #20 ANDk NIP ,&down JCN - #40 ANDk NIP ,&left JCN - #80 ANDk NIP ,&right JCN - POP - .Controller/key DEI - #20 EQUk NIP ,&play JCN - POP2 BRK - - &play - POP - DUP ;pos-to-addr JSR2 LDZk - #00 EQU ,&draw JCN - POP2 BRK - &draw - .game/current-player LDZ - SWP STZ - ;draw-move-at JSR2 - .game/moves LDZk INC SWP STZk - ( game can't be ended before 5 moves ) - POP #05 LTH ,&no-check JCN - ;check-win JSR2 - &no-check - BRK - &up - POP - DUP #00 ;draw-cursor-at JSR2 - ;move-cursor/up JSR2 - #01 ;draw-cursor-at JSR2 - BRK - &down - POP - DUP #00 ;draw-cursor-at JSR2 - ;move-cursor/down JSR2 - #01 ;draw-cursor-at JSR2 - BRK - &left - POP - DUP #00 ;draw-cursor-at JSR2 - ;move-cursor/left JSR2 - #01 ;draw-cursor-at JSR2 - BRK - &right - POP - DUP #00 ;draw-cursor-at JSR2 - ;move-cursor/right JSR2 - #01 ;draw-cursor-at JSR2 -BRK +@draw-cursor-at ( x y col -- ) + STH pos-to-xy OVR2 OVR2 .Screen/y DEO2 + .Screen/x DEO2 + #0e STHrk #01 draw-line #0e STHrk #02 draw-line .Screen/y DEO2 + .Screen/x DEO2 + #0e STHrk #02 draw-line #0f STHr #01 !draw-line -@on-controller-trap ( -> ) - .Controller/button DEI ,&restart JCN - .Controller/key DEI ,&restart JCN - BRK - &restart - #0100 JMP2 -BRK +@draw-move-at ( player x y -- ) + pos-to-xy + ( pad y ) #0003 ADD2 .Screen/y DEO2 + ( pad x ) #0003 ADD2 .Screen/x DEO2 + ( draw ) !draw-figure @check-win ( -- ) - ( diagonals ) - #00 ;pos-to-addr JSR2 LDZ - #11 ;pos-to-addr JSR2 LDZ - #22 ;pos-to-addr JSR2 LDZ AND AND - #02 ;pos-to-addr JSR2 LDZ - #11 ;pos-to-addr JSR2 LDZ - #20 ;pos-to-addr JSR2 LDZ AND AND - ORA + ( down diagonal ) .game/cells STHk #04 ADDk SWP ADDk NIP and3 + ( up diagonal ) STHrk INC INC #02 ADDk SWP ADDk NIP and3 + ( diagonals ) ORA + ( 1st column ) STHrk INCk INCk STHk and3 + ( 2nd column ) STHr INC INCk INCk STHk and3 + ( 3rd column ) STHr INC INCk INCk and3 + ( columns ) ORA ORA ORA + ( 1st row ) STHrk #03 ADDk SWP ADDk NIP and3 + ( 2nd row ) STHrk INC #03 ADDk SWP ADDk NIP and3 + ( 3rd row ) STHr INC INC #03 ADDk SWP ADDk NIP and3 + ( rows ) ORA ORA ORA + ( win ) DUP ?&endgame + ( draw ) .game/moves LDZ #09 EQU ?&endgame + POP JMP2r + &endgame ;on-reset/restart .Controller/vector DEO2 + #0023 .Screen/x DEO2 + #0043 .Screen/y DEO2 + ( >> ) - ( columns ) - #03 &while-x1 - #03 &while-y1 - DUP2 #01 DUP SUB2 - SWP #40 SFT ORA - ;pos-to-addr JSR2 LDZ STH - #01 SUB DUP ,&while-y1 JCN - POP - STH2r STHr AND AND STH - #01 SUB DUP ,&while-x1 JCN - POP - STH2r STHr ORA ORA - ORA +@draw-figure ( fig -- ) + #00 EQUk NIP ?&tie + #01 EQUk NIP ?&x + #02 EQUk NIP ?&o + ( should never happen ^tm^ ) POP JMP2r + &tie ( fig -- ) + POP + ( align ) .Screen/x DEI2k #0008 SUB2 ROT DEO2 + [ LIT2 05 -Screen/auto ] DEO + ;tie DUP2 .Screen/addr DEO2 + [ LIT2 01 -Screen/sprite ] DEOk + DEO + .Screen/addr DEO2 + [ LIT2 00 -Screen/auto ] DEO + [ LIT2 11 -Screen/sprite ] DEO + JMP2r + &x ( fig -- ) + ;x .Screen/addr DEO2 + .Screen/sprite DEO + ( | yes, drawing code does logic, i know ) + [ LIT2 02 -game/current-player ] STZk + JMP2r + &o ( fig -- ) + ;o .Screen/addr DEO2 + .Screen/sprite DEO + [ LIT2 01 -game/current-player ] STZ + JMP2r - ( rows ) - #03 &while-y2 - #03 &while-x2 - DUP2 #01 DUP SUB2 - #40 SFT ORA - ;pos-to-addr JSR2 LDZ STH - #01 SUB DUP ,&while-x2 JCN - POP - STH2r STHr AND AND STH - #01 SUB DUP ,&while-y2 JCN - POP - STH2r STHr ORA ORA - ORA - DUP ,&endgame JCN - .game/moves LDZ #09 EQU ,&endgame JCN - POP - JMP2r - &endgame - ;draw-endgame JSR2 - ;on-controller-trap .Controller/vector DEO2 -JMP2r +@draw-line-at ( x* y* len col dir -- ) + STH2 STH .Screen/y DEO2 + .Screen/x DEO2 + STHr STH2r + ( >> ) -@move-cursor ( pos -- newpos ) - &up - #03 ANDk NIP - #00 EQUk NIP #03 MUL ADD - #01 SUB SWP #30 AND ORA - ,&end JMP - &down - #03 ANDk NIP - #02 EQUk NIP #03 MUL SUB - INC SWP #30 AND ORA - ,&end JMP - &left - #04 SFTk NIP - #03 AND - #00 EQUk NIP #03 MUL ADD - #01 SUB #40 SFT - SWP #03 AND ORA - ,&end JMP - &right - #04 SFTk NIP - #03 AND - #02 EQUk NIP #03 MUL SUB - INC #40 SFT - SWP #03 AND ORA - &end - .game/cursor-pos STZk POP ( lil side effect : ) -JMP2r +@draw-line ( len col dir -- ) + .Screen/auto DEO + SWP &while SWP .Screen/pixel DEOk + POP SWP #01 SUB DUP ?&while + POP2 [ LIT2 00 -Screen/auto ] DEO + JMP2r -@draw-cursor-at ( pos col -- ) - STH - ;pos-to-xy JSR2 - OVR2 OVR2 - .Screen/y DEO2 - .Screen/x DEO2 - #0e STHrk #01 ;draw-line JSR2 - #0e STHrk #02 ;draw-line JSR2 - .Screen/y DEO2 - .Screen/x DEO2 - #0e STHrk #02 ;draw-line JSR2 - #0f STHr #01 ;draw-line JSR2 -JMP2r +( +@|utils ) -@draw-move-at ( pos -- ) - ;pos-to-xy JSR2 - #0003 ADD2 - .Screen/y DEO2 - #0003 ADD2 - .Screen/x DEO2 - .game/current-player LDZ - ;draw-figure JSR2 -JMP2r +@clear-cursor ( x y key -- x y ) + POP DUP2 #00 !draw-cursor-at -@draw-figure ( fig -- ) - #00 EQUk NIP ,&tie JCN - #01 EQUk NIP ,&x JCN - #02 EQUk NIP ,&o JCN - POP JMP2r ( this should never happen (tm) ) - - &tie - POP - .Screen/x DEI2k #0008 SUB2 ROT DEO2 ( align ) - #01 .Screen/auto DEO - ;tie DUP2 .Screen/addr DEO2 - #01 .Screen/sprite DEO - #0008 ADD2k NIP2 .Screen/addr DEO2 - #01 .Screen/sprite DEO - .Screen/addr DEO2 - #11 .Screen/sprite DEO - #00 .Screen/auto DEO - JMP2r - &x - POP - ;x .Screen/addr DEO2 - #01 .Screen/sprite DEO - #02 .game/current-player STZ ( evil stuff ) - JMP2r - &o - POP - ;o .Screen/addr DEO2 - #03 .Screen/sprite DEO - #01 .game/current-player STZ ( very evil ) -JMP2r +@coord-dec ( c -- c' ) + #00 EQUk NIP ?&end + #01 SUB &end JMP2r -@draw-endgame ( winstate -- ) - #13 ;pos-to-xy JSR2 - #0003 ADD2 - .Screen/y DEO2 - #0003 ADD2 - .Screen/x DEO2 - ;draw-figure JSR2 -JMP2r +@coord-inc ( c -- c' ) + #02 EQUk NIP ?&end + INC &end JMP2r -@draw-line ( wid col dir -- ) - .Screen/auto DEO - SWP - &while - SWP .Screen/pixel DEOk POP - SWP #01 SUB DUP ,&while JCN - POP2 - #00 .Screen/auto DEO -JMP2r +@and3 ( a b c -- x ) + LDZ SWP LDZ ROT LDZ AND AND JMP2r -@draw-line-at ( x* y* wid col dir -- ) - STH2 STH - .Screen/y DEO2 - .Screen/x DEO2 - STHr STH2r ;draw-line JSR2 -JMP2r +@pos-to-xy ( x y -- x* y* ) + ( y<<4 ) INC #00 SWP #40 SFT2 ROT + ( x<<4 ) INC #00 SWP #40 SFT2 SWP2 JMP2r -@pos-to-xy ( pos -- x* y* ) - #04 SFTk NIP - #03 AND - INC #00 SWP #40 SFT2 - ROT - #03 AND - INC #00 SWP #40 SFT2 -JMP2r +( +@|assets ) -@pos-to-addr ( pos -- addr ) - #04 SFTk NIP - #03 AND - #03 MUL - SWP - #03 AND - ADD - .game/cells ADD -JMP2r +@x + [ c3e7 7e3c 3c7e e7c3 ] -( data ) +@o + [ 7ee7 c3c3 c3c3 e77e ] -@x c3e7 7e3c 3c7e e7c3 -@o 7ee7 c3c3 c3c3 e77e -@tie 0000 e010 1008 0807 - 0824 1410 0146 3800 +@tie + [ + 0000 e010 1008 0807 0824 1410 0146 3800 ] -( vim: fdm=marker: )