|
|
@ -40,8 +40,6 @@ |
|
|
|
;; This fixed GPU "coil whine" which I was getting running at 5400hz (completely unnecessary) |
|
|
|
(var todo-arbitrary-delay-ms int 10) |
|
|
|
|
|
|
|
(var g-save-file-name (* (const char)) "progression.bin") |
|
|
|
|
|
|
|
(define-constant DATA_DIR "data/") |
|
|
|
|
|
|
|
(defmacro in-data-dir (path-in-data string) |
|
|
@ -505,37 +503,132 @@ |
|
|
|
|
|
|
|
(set g-action-buffer-write-head action-buffer-read-head)) |
|
|
|
|
|
|
|
;; Future work: A puzzle list ID would facilitate expansion puzzle databases not ruining progression |
|
|
|
(defstruct-local progression-puzzle |
|
|
|
index int ;; into g-puzzle-list |
|
|
|
is-solved bool) |
|
|
|
|
|
|
|
(var g-progression-puzzles (* progression-puzzle) null) |
|
|
|
(var g-num-progression-puzzles int 0) |
|
|
|
(var g-current-progression-puzzle (* progression-puzzle) null) |
|
|
|
|
|
|
|
(defun-local pick-random-progression-puzzle () |
|
|
|
(set g-current-progression-puzzle (addr (at (mod (rand) g-num-progression-puzzles) |
|
|
|
g-progression-puzzles)))) |
|
|
|
|
|
|
|
(defun-local game-board-load-next-puzzle () |
|
|
|
(when g-num-puzzles |
|
|
|
(set g-current-puzzle (addr (at (mod (rand) g-num-puzzles) g-puzzle-list))) |
|
|
|
(when g-num-progression-puzzles |
|
|
|
(pick-random-progression-puzzle) |
|
|
|
(var num-attempts int 1) |
|
|
|
(while (path g-current-progression-puzzle > is-solved) |
|
|
|
(pick-random-progression-puzzle) |
|
|
|
(incr num-attempts) |
|
|
|
;; Reasonable given how many puzzles someone might do in one sitting vs. total num puzzles |
|
|
|
(when (= num-attempts 200) |
|
|
|
(SDL_Log "Max attempts reached for random puzzle! Has the app not been restarted in a long time?\n") |
|
|
|
(break))) |
|
|
|
(unless (< (path g-current-progression-puzzle > index) g-num-puzzles) |
|
|
|
(SDL_Log "error: Progression puzzle index out of range!\n") |
|
|
|
(return)) |
|
|
|
(set g-current-puzzle (addr (at (path g-current-progression-puzzle > index) |
|
|
|
g-puzzle-list))) |
|
|
|
(game-board-load (path g-current-puzzle > board))) |
|
|
|
(set g-current-move-count 0) |
|
|
|
(set g-has-won-puzzle false) |
|
|
|
(reset-action-buffer)) |
|
|
|
|
|
|
|
(var g-progression-file-version (const int) 1) |
|
|
|
(var g-progression-file-version (const int) 2) |
|
|
|
(var g-progression-file-name ([] 512 char) (array 0)) |
|
|
|
|
|
|
|
(defun-local create-progression-puzzles () |
|
|
|
(when g-progression-puzzles (free (type-cast g-progression-puzzles (* void)))) |
|
|
|
(unless g-num-progression-puzzles |
|
|
|
(SDL_Log "error: expected g-num-progression-puzzles to be set before create-progression-puzzles\n") |
|
|
|
(return)) |
|
|
|
(set g-progression-puzzles |
|
|
|
(type-cast (calloc g-num-progression-puzzles |
|
|
|
(sizeof (type progression-puzzle))) (* progression-puzzle)))) |
|
|
|
|
|
|
|
;; If no previous progression is stored, we need to create the initial state from the puzzle |
|
|
|
;; database. All puzzles are added and marked as unsolved |
|
|
|
(defun-local initialize-progression-puzzles-from-database () |
|
|
|
(SDL_Log "Creating puzzle progression state from database\n") |
|
|
|
(unless g-num-puzzles |
|
|
|
(SDL_Log "error: no puzzles in database") |
|
|
|
(return)) |
|
|
|
(set g-num-progression-puzzles g-num-puzzles) |
|
|
|
(create-progression-puzzles) |
|
|
|
(each-in-range i g-num-progression-puzzles |
|
|
|
(set (field (at i g-progression-puzzles) index) i) |
|
|
|
(set (field (at i g-progression-puzzles) is-solved) false))) |
|
|
|
|
|
|
|
(defun-local set-progression-file-name () |
|
|
|
(if-c-preprocessor-defined |
|
|
|
__ANDROID__ |
|
|
|
;; Must save in this directory unless you're okay with the file being nuked on app update |
|
|
|
;; See e.g. http://www.dinomage.com/2013/05/howto-sdl-on-android-part-2-platform-details/ |
|
|
|
(snprintf g-progression-file-name (array-size g-progression-file-name) |
|
|
|
"%s/progression.bin" (SDL_AndroidGetInternalStoragePath)) |
|
|
|
(snprintf g-progression-file-name (array-size g-progression-file-name) |
|
|
|
"progression.bin"))) |
|
|
|
|
|
|
|
;; TODO: All of these reads and writes should check their error codes... |
|
|
|
(defun-local write-progression-data () |
|
|
|
(var save-file (* SDL_RWops) (SDL_RWFromFile g-save-file-name "w")) |
|
|
|
(if save-file |
|
|
|
(scope |
|
|
|
(SDL_WriteLE32 save-file g-progression-file-version) |
|
|
|
(SDL_WriteLE32 save-file g-num-puzzles-won) |
|
|
|
(SDL_RWclose save-file)) |
|
|
|
(SDL_Log "warning: failed to save progression file\n"))) |
|
|
|
(set-progression-file-name) |
|
|
|
(var progression-file (* SDL_RWops) (SDL_RWFromFile g-progression-file-name "w")) |
|
|
|
(if progression-file |
|
|
|
(scope |
|
|
|
(SDL_WriteLE32 progression-file g-progression-file-version) |
|
|
|
(SDL_WriteLE32 progression-file g-num-puzzles-won) |
|
|
|
(SDL_WriteLE32 progression-file g-num-progression-puzzles) |
|
|
|
;; Write all of them, even solved ones; read-progression-data will read them one-by-one and |
|
|
|
;; remove solved puzzles |
|
|
|
(SDL_RWwrite progression-file g-progression-puzzles (sizeof (type progression-puzzle)) |
|
|
|
g-num-progression-puzzles) |
|
|
|
(SDL_RWclose progression-file)) |
|
|
|
(SDL_Log "warning: failed to save progression file\n"))) |
|
|
|
|
|
|
|
(defun-local read-progression-data () |
|
|
|
(var save-file (* SDL_RWops) (SDL_RWFromFile g-save-file-name "r")) |
|
|
|
(if save-file |
|
|
|
(set-progression-file-name) |
|
|
|
(var progression-file (* SDL_RWops) (SDL_RWFromFile g-progression-file-name "r")) |
|
|
|
(if progression-file |
|
|
|
(scope |
|
|
|
(var version int (SDL_ReadLE32 save-file)) |
|
|
|
(var version int (SDL_ReadLE32 progression-file)) |
|
|
|
(unless (= version g-progression-file-version) |
|
|
|
(SDL_Log "warning: failed to load progression file (version mismatch). Progress will be lost\n") |
|
|
|
(SDL_RWclose save-file) |
|
|
|
(initialize-progression-puzzles-from-database) |
|
|
|
(SDL_RWclose progression-file) |
|
|
|
(return)) |
|
|
|
(set g-num-puzzles-won (SDL_ReadLE32 save-file)) |
|
|
|
(SDL_RWclose save-file)) |
|
|
|
(SDL_Log "warning: failed to load progression file\n"))) |
|
|
|
(set g-num-puzzles-won (SDL_ReadLE32 progression-file)) |
|
|
|
|
|
|
|
;; Read the puzzles in, filtering out completed ones |
|
|
|
(set g-num-progression-puzzles (SDL_ReadLE32 progression-file)) |
|
|
|
(create-progression-puzzles) |
|
|
|
(var num-unsolved-puzzles int 0) |
|
|
|
(each-in-range |
|
|
|
i g-num-progression-puzzles |
|
|
|
(var current-puzzle progression-puzzle) |
|
|
|
(SDL_RWread progression-file (addr current-puzzle) (sizeof current-puzzle) 1) |
|
|
|
(when (field current-puzzle is-solved) |
|
|
|
(continue)) |
|
|
|
(set (at num-unsolved-puzzles g-progression-puzzles) current-puzzle) |
|
|
|
(incr num-unsolved-puzzles)) |
|
|
|
|
|
|
|
;; Free empty space from all the solved puzzles last time the game was played |
|
|
|
(when (and g-num-progression-puzzles |
|
|
|
(!= g-num-progression-puzzles num-unsolved-puzzles)) |
|
|
|
(set g-progression-puzzles |
|
|
|
(type-cast (realloc g-progression-puzzles num-unsolved-puzzles) |
|
|
|
(* progression-puzzle))) |
|
|
|
(set g-num-progression-puzzles num-unsolved-puzzles)) |
|
|
|
|
|
|
|
(SDL_Log "%d unsolved puzzles remaining\n" g-num-progression-puzzles) |
|
|
|
|
|
|
|
(SDL_RWclose progression-file)) |
|
|
|
(scope ;; File didn't exist or something |
|
|
|
(SDL_Log "warning: failed to load progression file. This is fine if it's the first run\n") |
|
|
|
(initialize-progression-puzzles-from-database)))) |
|
|
|
|
|
|
|
;; |
|
|
|
;; UI (immediate-mode) |
|
|
|
;; |
|
|
@ -829,6 +922,7 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
(sdl-print-time-delta start-load-ticks "Puzzles loaded") |
|
|
|
|
|
|
|
(read-progression-data) |
|
|
|
(sdl-print-time-delta start-load-ticks "Progression loaded") |
|
|
|
|
|
|
|
(var main-menu-texture (* SDL_Texture) |
|
|
|
(sdl-texture-from-bmp (in-data-dir "MainMenu.bmp") renderer)) |
|
|
@ -1123,6 +1217,8 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
(unless g-has-won-puzzle |
|
|
|
(set g-has-won-puzzle true) |
|
|
|
(incr g-num-puzzles-won) |
|
|
|
(when g-current-progression-puzzle |
|
|
|
(set (path g-current-progression-puzzle > is-solved) true)) |
|
|
|
(write-progression-data)) |
|
|
|
(var dest-rect SDL_Rect (array 60 140 960 500)) |
|
|
|
(unless (= 0 (SDL_RenderCopy renderer win-texture null (addr dest-rect))) |
|
|
@ -1196,9 +1292,7 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
(array Keyword " " -1) |
|
|
|
(array Expression null 2) |
|
|
|
(array Keyword "\n" -1))) |
|
|
|
(return (CStatementOutput environment context tokens startTokenIndex |
|
|
|
define-statement (array-size define-statement) |
|
|
|
output))) |
|
|
|
(return (c-statement-out define-statement))) |
|
|
|
|
|
|
|
(defgenerator undefine-constant (define-name symbol) |
|
|
|
(var define-statement (const ([] CStatementOperation)) |
|
|
@ -1206,9 +1300,7 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
(array Keyword "#undef" -1) |
|
|
|
(array Expression null 1) |
|
|
|
(array Keyword "\n" -1))) |
|
|
|
(return (CStatementOutput environment context tokens startTokenIndex |
|
|
|
define-statement (array-size define-statement) |
|
|
|
output))) |
|
|
|
(return (c-statement-out define-statement))) |
|
|
|
|
|
|
|
;; Necessary to create e.g. in C PREFIX "_my_thing" |
|
|
|
(defgenerator static-string-combine (string-A any string-B any) |
|
|
@ -1217,18 +1309,50 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
(array Expression null 1) |
|
|
|
(array Keyword " " -1) |
|
|
|
(array Expression null 2))) |
|
|
|
(return (CStatementOutput environment context tokens startTokenIndex |
|
|
|
statement (array-size statement) |
|
|
|
output))) |
|
|
|
(return (c-statement-out statement))) |
|
|
|
|
|
|
|
;; 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))) |
|
|
|
(return (c-statement-out statement))) |
|
|
|
|
|
|
|
(defgenerator if-c-preprocessor-defined (preprocessor-symbol symbol |
|
|
|
true-block (index any) false-block (index any)) |
|
|
|
;; I had to use addStringOutput because CStatementOutput doesn't yet have a operation for |
|
|
|
;; statements, i.e., arguments which should be evaluated in Body scope |
|
|
|
(addStringOutput (field output source) "#ifdef" StringOutMod_SpaceAfter |
|
|
|
(addr (at startTokenIndex tokens))) |
|
|
|
(addStringOutput (field output source) (path preprocessor-symbol > contents) |
|
|
|
StringOutMod_NewlineAfter |
|
|
|
(addr (at startTokenIndex tokens))) |
|
|
|
|
|
|
|
(var true-context EvaluatorContext context) |
|
|
|
(set (field true-context scope) EvaluatorScope_Body) |
|
|
|
(unless (= 0 (EvaluateGenerate_Recursive environment true-context tokens true-block output)) |
|
|
|
(return false)) |
|
|
|
|
|
|
|
(addStringOutput (field output source) "#else" StringOutMod_NewlineAfter |
|
|
|
(addr (at startTokenIndex tokens))) |
|
|
|
|
|
|
|
(var false-context EvaluatorContext context) |
|
|
|
(set (field false-context scope) EvaluatorScope_Body) |
|
|
|
(unless (= 0 (EvaluateGenerate_Recursive environment false-context tokens false-block output)) |
|
|
|
(return false)) |
|
|
|
|
|
|
|
(addStringOutput (field output source) "#endif" StringOutMod_NewlineAfter |
|
|
|
(addr (at startTokenIndex tokens))) |
|
|
|
(return true)) |
|
|
|
|
|
|
|
(defmacro c-statement-out (statement-operation symbol) |
|
|
|
(tokenize-push |
|
|
|
output |
|
|
|
(CStatementOutput environment context tokens startTokenIndex |
|
|
|
(token-splice statement-operation) |
|
|
|
(array-size (token-splice statement-operation)) |
|
|
|
output)) |
|
|
|
(return true)) |
|
|
|
|
|
|
|
(defmacro repeat (thing any) |
|
|
|
(tokenize-push |
|
|
@ -1250,12 +1374,22 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
(return true)) |
|
|
|
|
|
|
|
(defmacro each-in-array (array-name symbol iterator-name symbol &rest body any) |
|
|
|
(tokenize-push |
|
|
|
output |
|
|
|
(scope |
|
|
|
(var num-elements int (array-size (token-splice array-name))) |
|
|
|
(each-in-range (token-splice iterator-name) num-elements |
|
|
|
(token-splice-rest body tokens)))) |
|
|
|
(return true)) |
|
|
|
|
|
|
|
;; TODO: Needs temp var to prevent double eval if range is an expression |
|
|
|
(defmacro each-in-range (iterator-name symbol range symbol &rest body any) |
|
|
|
(tokenize-push |
|
|
|
output |
|
|
|
(scope |
|
|
|
(var (token-splice iterator-name) int 0) |
|
|
|
(var next-iterator int 0) ;; TODO needs unique name |
|
|
|
(while (< next-iterator (array-size (token-splice array-name))) |
|
|
|
(while (< next-iterator (token-splice range)) |
|
|
|
(set (token-splice iterator-name) next-iterator) |
|
|
|
(incr next-iterator) |
|
|
|
(token-splice-rest body tokens)))) |
|
|
@ -1278,21 +1412,24 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
;; Building |
|
|
|
;; |
|
|
|
(defun-comptime generate-puzzles-list (manager (& ModuleManager) module (* Module) &return bool) |
|
|
|
(var puzzles-text (* (const char)) "../../data/puzzles.txt") |
|
|
|
(var puzzles-binary (* (const char)) "../../data/puzzles.bin") |
|
|
|
|
|
|
|
;; Already built? |
|
|
|
(when (fileExists "../../data/puzzles.bin") |
|
|
|
(unless (fileIsMoreRecentlyModified puzzles-text puzzles-binary) |
|
|
|
(Log "generate-puzzles-list: Puzzles list already built\n") |
|
|
|
(return true)) |
|
|
|
|
|
|
|
(unless (fileExists "../../data/puzzles.txt") |
|
|
|
(unless (fileExists puzzles-text) |
|
|
|
(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") |
|
|
|
(unless (fileExists puzzles-text) |
|
|
|
(Logf "generate-puzzles-list: Successfully executed Decompression, but didn't find |
|
|
|
%s. Are paths incorrect?\n" puzzles-text) |
|
|
|
(return false))) |
|
|
|
|
|
|
|
(run-process-sequential-or |
|
|
@ -1301,7 +1438,7 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
(Log "generate-puzzles-list: Failed to run PuzzleIO for puzzle database reading\n") |
|
|
|
(return false)) |
|
|
|
|
|
|
|
(unless (fileExists "../../data/puzzles.bin") |
|
|
|
(unless (fileExists puzzles-binary) |
|
|
|
(Log "generate-puzzles-list: Successfully executed PuzzleIO, but didn't find |
|
|
|
data/puzzles.bin. Are paths incorrect?\n") |
|
|
|
(return false)) |
|
|
@ -1316,7 +1453,7 @@ Rush Hour database from Michael Fogleman.\n\n") |
|
|
|
;; Bug in cakelisp where hooks aren't ordered predictably |
|
|
|
(unless (fileExists "../../data/puzzles.bin") |
|
|
|
(Log "copy-src-files-to-android: Didn't find data/puzzles.bin. Has generate-puzzles-list not |
|
|
|
been executed yet?\n") |
|
|
|
been executed yet? This might be hitting a bug in Cakelisp where hooks aren't ordered properly.\n") |
|
|
|
(return false)) |
|
|
|
|
|
|
|
(run-process-sequential-or |
|
|
@ -1343,7 +1480,8 @@ This tool requires rsync to be installed.\n") |
|
|
|
(comptime-cond |
|
|
|
('Kitty-Main |
|
|
|
(add-compile-time-hook-module pre-build generate-puzzles-list) |
|
|
|
(add-compile-time-hook-module pre-build copy-src-files-to-android))) |
|
|
|
(add-compile-time-hook-module pre-build copy-src-files-to-android) |
|
|
|
)) |
|
|
|
|
|
|
|
(module-use-sdl-build-options) |
|
|
|
|
|
|
|