@ -11,8 +11,8 @@
"../../src" )
( import "SDL.cake" "Math.cake" ;; GameLib
"Decompression.cake" ;; kitty-gridlock
&comptime-only "Macros.cake" ) ;; cakelisp/runtime
;; "Decompression.cake" ;; kitty-gridlock ;; Not used for runtime game
&comptime-only "Macros.cake" "BuildTools.cake" ) ;; cakelisp/runtime
( c-import "<stdio.h>" "<stdlib.h>"
"SDL.h" "SDL_syswm.h" "SDL_timer.h" "SDL_render.h"
@ -89,10 +89,12 @@
label char )
;; Pieces are stored separately, then their positions inform occupied state, which is used to check
;; movement constraints. -1 = empty cell. Values are indices into g-game-board-pieces. Used to
;; movement constraints. g-empty-cell = empty cell. Values are indices into g-game-board-pieces. Used to
;; easily pick from tap/click
( def-type-alias BoardPieceIndex char )
( var g-game-board-spatial-state ( [] 6 ( [] 6 BoardPieceIndex ) ) ( array 0 ) )
;; Previously, I used -1 as empty cell. On Android, it did not like that, and I'm not sure why
( var g-empty-cell char 127 )
( var g-game-board-spatial-state ( [] 6 ( [] 6 BoardPieceIndex ) ) ( array g-empty-cell ) )
;; Max pieces is determined as follows:
;; 6x6 grid = 36 squares
@ -137,27 +139,27 @@
( 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 ". " ) )
( SDL_Log "%c " ( ? label label ' #' ) ) )
( SDL_Log ". " ) )
( incr column ) )
( printf "\n" )
( SDL_Log "\n" )
( incr row ) ) )
( defun-local print-board-piece ( piece ( * ( const board-piece ) ) index int )
( printf " Piece %c ( [%d] %p )
( SDL_Log " Piece %c ( [%d] %p )
\n\tgrid-position %d %d
\n\tmoving-position %f %f
\n\tnum-cells %d
\n\tis-vertical %d
\n\tis-wall %d
\n\tis-primary-piece %d\n\n "
( path piece > label ) index piece
( field ( path piece > grid-position ) X ) ( field ( path piece > grid-position ) Y )
( field ( path piece > moving-position ) X ) ( field ( path piece > moving-position ) Y )
( path piece > num-cells )
( path piece > is-vertical )
( path piece > is-wall )
( path piece > is-primary-piece ) ) )
( path piece > label ) index piece
( field ( path piece > grid-position ) X ) ( field ( path piece > grid-position ) Y )
( field ( path piece > moving-position ) X ) ( field ( path piece > moving-position ) Y )
( path piece > num-cells )
( path piece > is-vertical )
( path piece > is-wall )
( path piece > is-primary-piece ) ) )
( defun-local is-within-grid ( coordinate grid-vec2 &return bool )
( return ( and ( >= ( vec-x coordinate ) 0 )
@ -167,7 +169,7 @@
( defun-local game-board-sync-occupied-state ( &return bool )
;; Zero out to make overlap validation easy
( memset g-game-board-spatial-state -1
( memset g-game-board-spatial-state g-empty-cell
( sizeof g-game-board-spatial-state ) )
( on-each-existing-board-piece
piece piece-index
@ -178,16 +180,16 @@
( scope ;; Validate that piece does not go off board
( unless ( is-within-grid ( path piece > grid-position ) )
( printf "error: Piece %p origin is off board! Must abort occupied state sync\n" piece )
( SDL_Log "error: Piece %p origin is off board! Must abort occupied state sync\n" piece )
( return false ) )
( if ( path piece > is-vertical )
( block ;; Vertical
( when ( > ( + num-cells ( vec-y ( path piece > grid-position ) ) ) g-game-board-grid-size )
( printf "error: Piece %p vertical is off board! Must abort occupied state sync\n" piece )
( SDL_Log "error: Piece %p vertical is off board! Must abort occupied state sync\n" piece )
( return false ) ) )
( block ;; Horizontal
( when ( > ( + num-cells ( vec-x ( path piece > grid-position ) ) ) g-game-board-grid-size )
( printf "error: Piece %p horizontal is off board! Must abort occupied state sync\n" piece )
( SDL_Log "error: Piece %p horizontal is off board! Must abort occupied state sync\n" piece )
( return false ) ) ) ) )
( var cell-offset int 0 )
@ -203,10 +205,10 @@
( var occupy-space-pointer ( * BoardPieceIndex )
( addr ( at ( vec-y cell-to-set ) ( vec-x cell-to-set ) g-game-board-spatial-state ) ) )
( when ( != -1 ( deref occupy-space-pointer ) )
( printf "error: Piece %d overlapping %d at (%d, %d)! Aborting\n"
piece-index ( deref occupy-space-pointer )
( vec-xy cell-to-set ) )
( when ( != g-empty-cell ( deref occupy-space-pointer ) )
( SDL_Log "error: Piece %d overlapping %d at (%d, %d)! Aborting\n"
piece-index ( deref occupy-space-pointer )
( vec-xy cell-to-set ) )
( return false ) )
( set ( deref occupy-space-pointer ) piece-index )
( incr cell-offset ) ) )
@ -293,15 +295,16 @@
( defun-local read-puzzles ( &return bool )
( var puzzles-file ( * FILE ) ( fopen ( in-data-dir "puzzles.txt" ) "r" ) )
( unless puzzles-file
( printf "Failed to load puzzles. You may need to run Decompression.cake (see Build.sh)\n" )
( return false ) )
( SDL_Log " Failed to load puzzles. Did the pre-build step generate-puzzles-list fail? You can
also run Decompression.cake directly ( see Build.sh ) \n " )
( return true ) )
;; 48 = max length of a single line in puzzles .txt
( var line-buffer ( [] 48 char ) ( array 0 ) )
( unless ( fgets line-buffer ( array-size line-buffer ) puzzles-file )
( printf "Puzzle file malformed\n" ) ( return false ) )
( SDL_Log "Puzzle file malformed\n" ) ( return false ) )
( var num-puzzles int ( atoi line-buffer ) )
( printf "Reading %d puzzles\n" num-puzzles )
( SDL_Log "Reading %d puzzles\n" num-puzzles )
( when g-puzzle-list ( free ( type-cast g-puzzle-list ( * void ) ) ) )
( set g-num-puzzles num-puzzles )
( set g-puzzle-list ( type-cast ( calloc g-num-puzzles ( sizeof ( type puzzle-data ) ) ) ( * puzzle-data ) ) )
@ -373,7 +376,7 @@
( var piece-top-left-px vec2 ( game-piece-position-to-screen-position piece ) )
( var piece-bottom-right-px vec2 ( game-piece-get-screen-size piece ) )
( set piece-bottom-right-px ( vec2-add piece-top-left-px piece-bottom-right-px ) )
;; (printf "%f %f -> %f %f vs. (%f %f)\n" (vec-xy piece-top-left-px) (vec-xy piece-bottom-right-px)
;; (SDL_Log "%f %f -> %f %f vs. (%f %f)\n" (vec-xy piece-top-left-px) (vec-xy piece-bottom-right-px)
;; (vec-xy screen-point-to-test))
( return
( and
@ -393,7 +396,7 @@
( var piece-index char
( at ( vec-y grid-position ) ( vec-x grid-position ) g-game-board-spatial-state ) )
( unless ( != -1 piece-index )
( unless ( != g-empty-cell piece-index )
( return null ) )
( var piece ( * board-piece ) ( addr ( at piece-index g-game-board-pieces ) ) )
( unless ( is-within-game-piece piece position )
@ -453,7 +456,7 @@
( while ( is-within-grid scan-position )
( var space-index int ( at ( vec-y scan-position ) ( vec-x scan-position )
g-game-board-spatial-state ) )
( if ( = -1 space-index )
( if ( = g-empty-cell space-index )
( block
( if ( path direction > is-increasing )
( set max-position ( grid-vec2-add max-position ( path direction > delta ) ) )
@ -478,7 +481,7 @@
( var win-square-index BoardPieceIndex ( at ( vec-y g-game-board-win-cell )
( vec-x g-game-board-win-cell )
g-game-board-spatial-state ) )
( when ( != -1 win-square-index )
( when ( != g-empty-cell win-square-index )
( var piece ( * board-piece ) ( addr ( at win-square-index g-game-board-pieces ) ) )
( return ( path piece > is-primary-piece ) ) )
( return false ) )
@ -487,21 +490,22 @@
( var g-current-puzzle ( * puzzle-data ) null )
( defun game-board-load-next-puzzle ( )
( set g-current-puzzle ( addr ( at ( mod ( rand ) g-num-puzzles ) g-puzzle-list ) ) )
( game-board-load ( path g-current-puzzle > board ) ) )
( when g-num-puzzles
( set g-current-puzzle ( addr ( at ( mod ( rand ) g-num-puzzles ) g-puzzle-list ) ) )
( game-board-load ( path g-current-puzzle > board ) ) ) )
;;
;; UI (immediate-mode)
;;
( defstruct-local input-state
pointer-position vec2
is-pointer-pressed bool
was-clicked bool ) ;; Click on pointer release
pointer-position vec2
is-pointer-pressed bool
was-clicked bool ) ;; Click on pointer release
( defun-local update-input-state ( in-state ( * input-state ) mouse-position vec2 is-pressed bool )
( set ( path in-state > pointer-position ) mouse-position )
( set ( path in-state > was-clicked ) ( and ( path in-state > is-pointer-pressed )
( not is-pressed ) ) )
( not is-pressed ) ) )
( set ( path in-state > is-pointer-pressed ) is-pressed ) )
( defun-local is-within-aabb ( point-to-test vec2 upper-left vec2 size vec2 &return bool )
@ -588,22 +592,22 @@
( var driver-info SDL_RendererInfo ( array 0 ) )
( unless ( = 0 ( SDL_GetRenderDriverInfo i ( addr driver-info ) ) )
( return false ) )
( printf " Renderer [%d]: %s\n
( SDL_Log " Renderer [%d]: %s\n
\tHardware accelerated: %s\n
\tRender to texture: %s\n
\tMax texture width: %d\n
\tMax texture height: %d\n
\n "
i ( field driver-info name )
( ? ( bit-and ( field driver-info flags ) SDL_RENDERER_ACCELERATED ) "yes" "no" )
( ? ( bit-and ( field driver-info flags ) SDL_RENDERER_TARGETTEXTURE ) "yes" "no" )
( field driver-info max_texture_width )
( field driver-info max_texture_height ) )
i ( field driver-info name )
( ? ( bit-and ( field driver-info flags ) SDL_RENDERER_ACCELERATED ) "yes" "no" )
( ? ( bit-and ( field driver-info flags ) SDL_RENDERER_TARGETTEXTURE ) "yes" "no" )
( field driver-info max_texture_width )
( field driver-info max_texture_height ) )
( incr i ) )
( var macoy-beast-driver ( const int ) 0 )
( var selected-renderer int macoy-beast-driver )
( printf "Using renderer %d\n" selected-renderer )
( SDL_Log "Using renderer %d\n" selected-renderer )
( set ( deref renderer-out ) ( SDL_CreateRenderer window selected-renderer SDL_RENDERER_ACCELERATED ) )
( unless ( deref renderer-out )
( sdl-print-error )
@ -614,7 +618,7 @@
&return ( * SDL_Texture ) )
( var surface ( * SDL_Surface ) ( SDL_LoadBMP filename ) )
( unless surface
( printf "Failed to load surface\n" )
( SDL_Log "Failed to load surface from BMP %s \n" filename )
( sdl-print-error )
( return null ) )
@ -630,7 +634,7 @@
&return ( * SDL_Texture ) )
( var surface ( * SDL_Surface ) ( SDL_LoadBMP filename ) )
( unless surface
( printf "Failed to load surface\n" )
( SDL_Log "Failed to load surface from BMP %s \n" filename )
( sdl-print-error )
( return null ) )
@ -651,7 +655,7 @@
( var delta-time float ( / frame-diff-ticks
( type-cast performance-num-ticks-per-second float ) ) )
( printf "--- %s at %f seconds\n" label delta-time ) )
( SDL_Log "--- %s at %f seconds\n" label delta-time ) )
;; Factor 0 to 1
( defun-local vec2-interpolate ( factor float from vec2 to vec2 &return vec2 )
@ -669,8 +673,8 @@
;; Main
;;
( defun main ( &return int )
( printf " Kitty Gridlock\n\n
( defun main ( num-args int args ( [] ( * char ) ) &return int )
( SDL_Log " Kitty Gridlock\n\n
Created by Macoy Madson <macoy@macoy.me>.\n
https://macoy.me/code/macoy/kitty-gridlock\n
Copyright ( c ) 2021 Macoy Madson.\n
@ -701,6 +705,12 @@ Rush Hour database from Michael Fogleman.\n\n")
;;
;; Initialization
;;
;; TODO: Not sure if necessary for Android, definitely not necessary for PC
( SDL_GL_SetAttribute SDL_GL_RED_SIZE 5 )
( SDL_GL_SetAttribute SDL_GL_GREEN_SIZE 6 )
( SDL_GL_SetAttribute SDL_GL_BLUE_SIZE 5 )
( var window ( * SDL_Window ) null )
( unless ( sdl-initialize-for-2d ( addr window ) "Kitty Gridlock"
g-window-width g-window-height )
@ -734,7 +744,7 @@ Rush Hour database from Michael Fogleman.\n\n")
( sdl-print-time-delta start-load-ticks "Loading screen displayed" )
;; (unless (bunzip-decompress puzzle-database-filename)
;; (return 1))
;; (return 1))
( unless ( read-puzzles ) ( return 1 ) )
( sdl-print-time-delta start-load-ticks "Puzzles loaded" )
@ -742,7 +752,7 @@ Rush Hour database from Michael Fogleman.\n\n")
renderer ) )
( unless background-texture ( return 1 ) )
( var background-night-texture ( * SDL_Texture ) ( sdl-texture-from-bmp ( in-data-dir "Board_Night.bmp" )
renderer ) )
renderer ) )
( unless background-night-texture ( return 1 ) )
( var grid-texture ( * SDL_Texture ) ( sdl-texture-from-bmp ( in-data-dir "Grid.bmp" )
renderer ) )
@ -750,7 +760,7 @@ Rush Hour database from Michael Fogleman.\n\n")
( var pieces-texture ( * SDL_Texture )
( sdl-texture-from-bmp-color-to-transparent
"assets/FromV/Exports/KittyGridlockInitialCatAssets.bmp"
( in-data-dir "Pieces.bmp" )
renderer
0xff 0x00 0xd3 ) )
( unless pieces-texture ( return 1 ) )
@ -801,8 +811,8 @@ Rush Hour database from Michael Fogleman.\n\n")
( srand ( type-cast ( SDL_GetPerformanceCounter ) int ) )
;; (game-board-load "IBBxooIooLDDJAALooJoKEEMFFKooMGGHHHM" )
( game-board-load-next-puzzle )
( game-board-load "IBBxooIooLDDJAALooJoKEEMFFKooMGGHHHM" )
;; (game-board-load-next-puzzle )
;;
;; Game loop
@ -852,7 +862,7 @@ Rush Hour database from Michael Fogleman.\n\n")
( array
( / ( to-float logical-width ) window-width )
( / ( to-float logical-height ) window-height ) ) )
;; (printf "Window scale %f %f\n" (vec-xy to-logical-scale))
;; (SDL_Log "Window scale %f %f\n" (vec-xy to-logical-scale))
( var logical-aspect-ratio float ( / logical-width ( to-float logical-height ) ) )
( var window-aspect-ratio float ( / window-width ( to-float window-height ) ) )
( if ( > window-aspect-ratio logical-aspect-ratio ) ;; Remove centering for correct scaling
@ -860,9 +870,9 @@ Rush Hour database from Michael Fogleman.\n\n")
( var total-margin float
( - window-width ( * window-width ( / logical-aspect-ratio window-aspect-ratio ) ) ) )
( set ( vec-x to-logical-scale ) ( / logical-width ( - window-width total-margin ) ) )
( var decentered float
( / total-margin
2.f ) )
( var decentered float
( / total-margin
2.f ) )
( set ( vec-x mouse-position )
( - ( vec-x mouse-position )
decentered ) ) )
@ -870,9 +880,9 @@ Rush Hour database from Michael Fogleman.\n\n")
( var total-margin float
( - window-height ( * window-height ( / window-aspect-ratio logical-aspect-ratio ) ) ) )
( set ( vec-y to-logical-scale ) ( / logical-height ( - window-height total-margin ) ) )
( var decentered float
( / total-margin
2.f ) )
( var decentered float
( / total-margin
2.f ) )
( set ( vec-y mouse-position )
( - ( vec-y mouse-position )
decentered ) ) ) )
@ -909,7 +919,7 @@ Rush Hour database from Michael Fogleman.\n\n")
( set exit-reason "Piece movement constraints failed to keep piece on board" )
( break ) )
( when ( or ( != 0 ( vec-x delta-grid-movement ) )
( != 0 ( vec-y delta-grid-movement ) ) )
( != 0 ( vec-y delta-grid-movement ) ) )
( incr current-move-count ) )
( set ( path selected-piece > grid-position ) new-position )
;; Remove the moving position difference from changing grid position, b/c moving is relative
@ -1038,24 +1048,24 @@ Rush Hour database from Michael Fogleman.\n\n")
( set delta-time ( / frame-diff-ticks
( type-cast performance-num-ticks-per-second float ) ) )
;; (printf "%lu %f %fhz\n" frame-diff-ticks delta-time (/ 1.f delta-time))
;; (SDL_Log "%lu %f %fhz\n" frame-diff-ticks delta-time (/ 1.f delta-time))
( SDL_Delay todo-arbitrary-delay-ms ) )
( scope ;; Frame time
( var num-timings int ( array-size recent-n-perf-counts ) )
( printf "Recent %d frame timings (fixed sleep of %d ms):\n" num-timings todo-arbitrary-delay-ms )
( SDL_Log "Recent %d frame timings (fixed sleep of %d ms):\n" num-timings todo-arbitrary-delay-ms )
( var i int 0 )
( while ( < i num-timings )
( var delta-time float ( / ( at i recent-n-perf-counts )
( type-cast performance-num-ticks-per-second float ) ) )
( printf "\t%f %fhz\n" delta-time ( / 1.f delta-time ) )
( SDL_Log "\t%f %fhz\n" delta-time ( / 1.f delta-time ) )
( incr i ) ) )
;;
;; Shutdown
;;
( when exit-reason
( printf "Exiting. Reason: %s\n" exit-reason ) )
( SDL_Log "Exiting. Reason: %s\n" exit-reason ) )
( when g-puzzle-list ( free ( type-cast g-puzzle-list ( * void ) ) ) )
@ -1078,7 +1088,7 @@ Rush Hour database from Michael Fogleman.\n\n")
( tokenize-push
output
( when debug-log-enabled
( printf ( token-splice-rest arguments tokens ) ) ) )
( SDL_Log ( token-splice-rest arguments tokens ) ) ) )
( return true ) )
( defgenerator define-constant ( define-name symbol value any )
@ -1147,8 +1157,54 @@ Rush Hour database from Michael Fogleman.\n\n")
;;
;; Building
;;
( defun-comptime generate-puzzles-list ( manager ( & ModuleManager ) module ( * Module ) &return bool )
;; Already built?
( when ( fileExists "../../data/puzzles.txt" )
( Log "generate-puzzles-list: Puzzles list already built\n" )
( return true ) )
( Log "generate-puzzles-list: Building puzzles list from database\n" )
( run-process-sequential-or
;; Note that we're still relative to Dependencies/gamelib
( "./Dependencies/cakelisp/bin/cakelisp" "--execute" "../../src/Decompression.cake" )
( Log "generate-puzzles-list: Failed to run Decompression for puzzle database reading\n" )
( return false ) )
( unless ( fileExists "../../data/puzzles.txt" )
( Log " generate-puzzles-list: Successfully executed Decompression, but didn 't find
data/puzzles.txt. Are paths incorrect?\n " )
( return false ) )
( return true ) )
( defun-comptime copy-src-files-to-android-hack ( manager ( & ModuleManager ) module ( * Module ) &return bool )
( Log "copy-src-files-to-android-hack: Copying files to SDL Android project\n" )
( run-process-sequential-or
;; Note that we're still relative to Dependencies/gamelib
( "rsync" "--verbose" "--recursive" "--update"
"../../data"
"Dependencies/SDL/build/org.libsdl.testgles/app/src/main/assets/" )
( Log " copy-src-files-to-android-hack: failed to sync data/ to Android assets folder\n
This tool requires rsync to be installed.\n " )
( return false ) )
( run-process-sequential-or
;; Note that we're still relative to Dependencies/gamelib
( "rsync" "--verbose" "--recursive" "--update"
"--exclude=*.o" "--exclude=kitty-gridlock" "--exclude=*Cache.cake"
"cakelisp_cache/HandmadeMath-Kitty/"
"Dependencies/SDL/build/org.libsdl.testgles/app/jni/src/" )
( Log " copy-src-files-to-android-hack: failed to sync src/ to Android assets folder\n
This tool requires rsync to be installed.\n " )
( return false ) )
( return true ) )
;; Order matters here, because we want to copy the generated puzzles list to android
( add-compile-time-hook-module pre-build generate-puzzles-list )
( add-compile-time-hook-module pre-build copy-src-files-to-android-hack )
( module-use-sdl-build-options )
;; Note that this executable still pulls .so files from Dependencies
( set-cakelisp-option executable-output "../../kitty-gridlock" )
;; Use build label to make it easier to find in cakelisp_cache
( add-build-config-label "Kitty" )