Browse Source

WIP saving of puzzles already done

This doesn't quite work yet because the progression file is read in
incorrectly, I think
Macoy Madson 2 years ago
  1. 7
  2. 218


@ -9,6 +9,10 @@
(add-c-build-dependency "bunzip-4.1.c") ;; LGPLv2
(var g-known-num-puzzles (const int) 2577412)
;; This controls how many should be exported!
(var g-num-puzzles-to-read (const int) 10)
(var-global g-bunzip-debug bool false)
(def-function-signature-global bunzip-decompress-callback (buffer (* char) num-bytes-this-read int
@ -74,9 +78,6 @@
(true ;; Generate puzzles.txt
(var g-known-num-puzzles (const int) 2577412)
(var g-num-puzzles-to-read (const int) 10000)
;; Note that this doesn't change the distribution of puzzles
(var g-puzzle-skip-count (const int) (/ g-known-num-puzzles g-num-puzzles-to-read))


@ -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)
(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
(var num-attempts int 1)
(while (path g-current-progression-puzzle > is-solved)
(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")
(unless (< (path g-current-progression-puzzle > index) g-num-puzzles)
(SDL_Log "error: Progression puzzle index out of range!\n")
(set g-current-puzzle (addr (at (path g-current-progression-puzzle > index)
(game-board-load (path g-current-puzzle > board)))
(set g-current-move-count 0)
(set g-has-won-puzzle false)
(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")
(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")
(set g-num-progression-puzzles g-num-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 ()
;; Must save in this directory unless you're okay with the file being nuked on app update
;; See e.g.
(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)
;; 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
(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")))
(var progression-file (* SDL_RWops) (SDL_RWFromFile g-progression-file-name "w"))
(if progression-file
(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))
(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
(var progression-file (* SDL_RWops) (SDL_RWFromFile g-progression-file-name "r"))
(if progression-file
(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)
(SDL_RWclose progression-file)
(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))
(var num-unsolved-puzzles int 0)
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)
(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")
;; UI (immediate-mode)
@ -829,6 +922,7 @@ Rush Hour database from Michael Fogleman.\n\n")
(sdl-print-time-delta start-load-ticks "Puzzles loaded")
(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))
(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)
(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)
(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)
(return (c-statement-out statement)))
;; cakelisp's tokenizer doesn't properly parse ' '
(defgenerator space-hack ()
(var statement (const ([] CStatementOperation))
(array Keyword "' '" -1)))
(return (CStatementOutput environment context tokens startTokenIndex
statement (array-size statement)
(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)
(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)
(CStatementOutput environment context tokens startTokenIndex
(token-splice statement-operation)
(array-size (token-splice statement-operation))
(return true))
(defmacro repeat (thing any)
@ -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)
(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)
(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")
;; 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)))
@ -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))
@ -1343,7 +1480,8 @@ This tool requires rsync to be installed.\n")
(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)