@ -16,6 +16,10 @@
;;
;; Constants
;;
( var debug-log-enabled bool true )
;; (var debug-log-enabled bool false)
( define-constant DATA_DIR "data/" )
( defmacro in-data-dir ( path-in-data string )
@ -76,7 +80,11 @@
;; 36 squares / 2 (min piece size) = 13; if two walls, need 14
( var g-game-board-pieces ( [] 14 board-piece ) ( array 0 ) )
( defmacro on-each-existing-board-piece ( piece-pointer-name symbol piece-index-name symbol &rest body any )
( defun-local game-board-reset-pieces ( )
( memset g-game-board-pieces 0 ( sizeof g-game-board-pieces ) ) )
( defmacro on-each-board-piece ( piece-pointer-name symbol piece-index-name symbol
&rest body any )
( tokenize-push
output
( var next-piece-index int 0 )
@ -86,10 +94,35 @@
( addr ( at next-piece-index g-game-board-pieces ) ) )
( set ( token-splice piece-index-name ) next-piece-index )
( incr next-piece-index ) ;; In case expansions include (continue), we've already incremented
( when ( path ( token-splice piece-pointer-name ) > num-cells )
( token-splice-rest body tokens ) ) ) )
( token-splice-rest body tokens ) ) )
( return true ) )
( defmacro on-each-existing-board-piece ( piece-pointer-name symbol piece-index-name symbol
&rest body any )
( tokenize-push
output
( on-each-board-piece
( token-splice piece-pointer-name ) ( token-splice piece-index-name )
( when ( path ( token-splice piece-pointer-name ) > num-cells )
( token-splice-rest body tokens ) ) ) )
( return true ) )
( defun-local game-board-print ( )
( var row int 0 )
( while ( < row g-game-board-grid-size )
( var column int 0 )
( while ( < column g-game-board-grid-size )
( var index BoardPieceIndex ( at row column g-game-board-occupied-state ) )
( if ( and ( >= index 0 ) ( field ( at index g-game-board-pieces ) num-cells ) )
( scope
( var piece-in-cell ( * board-piece ) ( addr ( at index g-game-board-pieces ) ) )
( var label char ( path piece-in-cell > label ) )
( printf "%c " ( ? label label ' #' ) ) )
( printf ". " ) )
( incr column ) )
( printf "\n" )
( incr row ) ) )
( defun-local print-board-piece ( piece ( * ( const board-piece ) ) index int )
( printf " Piece %c ( [%d] %p )
\n\tgrid-position %d %d
@ -112,7 +145,8 @@
( sizeof g-game-board-occupied-state ) )
( on-each-existing-board-piece
piece piece-index
( print-board-piece piece piece-index )
( when debug-log-enabled
( print-board-piece piece piece-index ) )
( var num-cells int ( path piece > num-cells ) )
@ -154,22 +188,100 @@
( set ( deref occupy-space-pointer ) piece-index )
( incr cell-offset ) ) )
;; Print board
( var row int 0 )
( while ( < row g-game-board-grid-size )
( var column int 0 )
( while ( < column g-game-board-grid-size )
( var index BoardPieceIndex ( at row column g-game-board-occupied-state ) )
( if ( and ( >= index 0 ) ( field ( at index g-game-board-pieces ) num-cells ) )
( scope
( var piece-in-cell ( * board-piece ) ( addr ( at index g-game-board-pieces ) ) )
( var label char ( path piece-in-cell > label ) )
( printf "%c " ( ? label label ' #' ) ) )
( printf ". " ) )
( incr column ) )
( printf "\n" )
( incr row ) )
( when debug-log-enabled
( game-board-print ) )
( return true ) )
( defun-local game-board-piece-from-char ( board-char char piece-cell-position grid-vec2 )
( when ( = board-char 'x ' ) ;; Handle walls specially
( on-each-board-piece
piece piece-index
( unless ( = 0 ( field ( deref piece ) label ) )
( continue ) )
( set ( field ( deref piece ) label ) board-char )
( set ( field ( deref piece ) grid-position ) piece-cell-position )
( set ( field ( deref piece ) is-wall ) true )
( incr ( field ( deref piece ) num-cells ) )
( break ) )
( return ) )
( on-each-board-piece
piece piece-index
( cond
( ( = board-char ( field ( deref piece ) label ) ) ;; Existing piece
( cond ;; Determine direction
( ( > ( vec-x piece-cell-position ) ( vec-x ( field ( deref piece ) grid-position ) ) )
( set ( field ( deref piece ) is-vertical ) false ) )
( ( > ( vec-y piece-cell-position ) ( vec-y ( field ( deref piece ) grid-position ) ) )
( set ( field ( deref piece ) is-vertical ) true ) ) )
( incr ( field ( deref piece ) num-cells ) )
( break ) )
( ( = 0 ( field ( deref piece ) label ) ) ;; New piece
( set ( field ( deref piece ) label ) board-char )
( set ( field ( deref piece ) grid-position ) piece-cell-position )
( set ( field ( deref piece ) is-primary-piece ) ( = board-char 'A ' ) )
( incr ( field ( deref piece ) num-cells ) )
( break ) ) ) ) )
;; (The following text is copied from [[https://www.michaelfogleman.com/rush/][Michael Fogleman's Rush Hour database]]
;; ([[https://web.archive.org/web/20201101044241/https://www.michaelfogleman.com/rush/][archive.org]]),
;; for easier reference)
;; The database is a simple text file with just a few columns. There is one row for every valid
;; (solvable, minimal) cluster. The columns are: number of moves, board description, and cluster
;; size (number of reachable states).
;; The board description is a 36-character string representing the state of the unsolved board. It
;; is a 6x6 2D array in row-major order. The characters in the description follow these simple rules:
;; - o empty cell
;; - x wall (fixed obstacle)
;; - A primary piece (red car)
;; - B - Z all other pieces
;; I used lowercase ~o~ instead of periods ~.~ for the empty cells in the database so that the entire board description can be selected with a double-click.
;; Example format:
;; 60 IBBxooIooLDDJAALooJoKEEMFFKooMGGHHHM 2332
( defun-local game-board-load ( fogleman-board-string ( * ( const char ) ) &return bool )
( game-board-reset-pieces )
( var num-moves int 0 )
( var num-states int 0 )
( var pieces-read bool false )
( var buffer ( [] 37 char ) ( array 0 ) )
( var write-char ( * char ) buffer )
( var current-char ( * ( const char ) ) fogleman-board-string )
( while ( deref current-char )
( cond
;; Space field delimiter
( ( = ( deref current-char ) ( space-hack ) )
( cond
( ( not num-moves )
( set num-moves ( atoi buffer ) ) )
( true ;; Reading pieces
( var board-char ( * ( const char ) ) buffer )
( while ( deref board-char )
( cond
( ( = ( deref board-char ) 'o ' )
( debug-log "empty\n" ) ) ;; Empty space
( true ;; Wall or piece
( var char-index int ( - board-char buffer ) )
( var piece-cell-position grid-vec2 ( array ( % char-index g-game-board-grid-size )
( / char-index g-game-board-grid-size ) ) )
( debug-log "[%d] '%c' = (%d %d)\n" char-index ( deref board-char ) ( vec-xy piece-cell-position ) )
( game-board-piece-from-char ( deref board-char ) piece-cell-position ) ) )
( incr board-char ) ) ) )
;; Reset buffer
( memset buffer 0 ( sizeof buffer ) )
( set write-char buffer ) )
( true
( set ( deref write-char ) ( deref current-char ) )
( incr write-char ) ) )
( incr current-char ) )
( set num-states ( atoi buffer ) )
( game-board-sync-occupied-state )
( printf "Board loaded. %d moves, %d states.\n" num-moves num-states )
( printf "%s\n" fogleman-board-string )
( return true ) )
;;
@ -278,6 +390,8 @@ Rush Hour database from Michael Fogleman.\n\n")
( unless ( game-board-sync-occupied-state )
( return 1 ) )
( game-board-load "60 IBBxooIooLDDJAALooJoKEEMFFKooMGGHHHM 2332" )
;;
;; Game loop
;;
@ -308,6 +422,17 @@ Rush Hour database from Michael Fogleman.\n\n")
( sdl-shutdown window )
( return 0 ) )
;;
;; Macros and generators
;;
( defmacro debug-log ( &rest arguments any )
( tokenize-push
output
( when debug-log-enabled
( printf ( token-splice-rest arguments tokens ) ) ) )
( return true ) )
( defgenerator define-constant ( define-name symbol value any )
( var define-statement ( const ( [] CStatementOperation ) )
( array
@ -331,6 +456,15 @@ Rush Hour database from Michael Fogleman.\n\n")
statement ( array-size statement )
output ) ) )
;; cakelisp's tokenizer doesn't properly parse ' '
( defgenerator space-hack ( )
( var statement ( const ( [] CStatementOperation ) )
( array
( array Keyword "' '" -1 ) ) )
( return ( CStatementOutput environment context tokens startTokenIndex
statement ( array-size statement )
output ) ) )
;;
;; Building
;;