Copyright (c) 2023 Zsombor Török
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
It is Sokoban but spelled with sch because Schönherz.
This developer's documentation is only a complementary resource to the already documented source code.
GitHub repository for the project: zsotroav/Schokoban
The project follows a simple folder structure with C source code in the
src
directory and related header files in the include
directory.
schokoban
├── src
| ├── data.c // Basic data handling
| ├── game.c // Main gameplay logic
| ├── io_level.c // Level (XSB) file path and location handling
| ├── io_map.c // Level (XSB) file reading
│ ├── main.c // Main entry code and gameplay loop
| ├── menu_custom.c // Custom map select menu handling
| ├── menu_level_handle.c // Level select menu handling
| ├── menu_level_printer.c // Level select menu printer functions
| ├── menu_main_handle.c // Main menu movement handling
| ├── menu_main_printer.c // Main menu printer functions
| └── printer.c // Game UI Handling
├── include
| ├── lib // Libraries
| | ├── debugmalloc.h
| │ ├── ECONIO_LICENSE // MIT License file for the econio library
| │ ├── econio.c
| │ └── econio.h
| ├── config.h // Global configuration constants (instead of .ENV)
| └── ...
├── .gitignore
├── CMakeLists.txt
├── DEV_DOC.md
├── econio_readme.md // Readme for econio lib may get removed in the future
├── LICENSE // GPL-3.0 (License file)
├── spec_ui.png
└── spec.md // Specifications for the game
- Libraries:
- c-econio by Zoltan Czirkos. MIT License
- debugmalloc
- Code snippets: Noted in source if applicable
- General reference documents:
- Sokoban.org for game rules and XSB/LURD format specs
- Miscellaneous:
- ASCII font for logo: tmplr by Eugene Ghanizadeh Khoub, generated with patorjk.com
Defined in data.h
, struct map_data
contains all stored information about
the currently loaded level/map. Most function calls require a map_data
pointer to operate.
tpye | field | description |
---|---|---|
int | level | Level number used if the game type is arcade. |
char* | loc | Location of the map data file. |
map_char* | map | Display data. |
int | width | Map's width. |
int | height | Map's height. |
fame* | fame_list | Fame List |
char* | title | Map's title metadata. |
char* | author | Map's author metadata. |
bool | functional | Indicate if an internal error was encountered. |
int | move_cnt | Number of moves made (to not have to traverse the linked list each time it's needed) |
move* | moves | List of made moves. |
int | player_x | Player character's current X location. |
int | player_y | Player character's current Y location. |
int | box | Number of boxes not on goals yet. |
Defined in data.h
, enum move_type
is an enum for the types of moves possible
in the LURD format. Each possible state has an assigned char value as well.
enum | char | decription |
---|---|---|
MV_L | L | Left (box moved) |
MV_U | U | Up (box moved) |
MV_R | R | Right (box moved) |
MV_D | D | Down (box moved) |
MV_l | l | Left |
MV_u | u | Up |
MV_r | r | Right |
MV_d | d | Down |
MV_UNDO | x | Undo |
MV_INV | Invalid move (reserved) |
Defined in data.h
, struct move
is a double linked list for storing the list
of moves made by the player on the current level
type | field | description |
---|---|---|
move_type | type | Type of move made |
move* | prev | Previous list item |
fame* | next | Next list item |
Defined in data.h
, struct fame
is a linked list for storing the fame list
type | field | description |
---|---|---|
char* | name | Name of the player |
int | move | Number of moves |
fame* | next | Pointer to next list item |
The following figures show a high-level (simplified) overview of the game's underlying logic and structure, showing only the important connections and relations.
Solid lines represent function calls, while dotted lines show code execution order; highlighted boxes are drawn around functions from the same file or for visual separation.
See Figures 4.2-4.6 for a more detailed overview of the main gameplay loop.
flowchart TD
main([GAME OPENED]) --> menu{main menu\nGame mode?}
menu --> |ARCADE| game_master
menu --> |FREE| menu_level_open --> game_master
menu --> |CUSTOM| menu_custom_open --> game_master
game_master -.-> |Init failed\nreturn| menu
menu --> |exit| exit([EXIT GAME])
game_master --> |Prepare the game\nand enter main gameplay loop| game_loop
subgraph game_loop[Execution order]
direction LR
init>Initialize the game]
-.-> master_loaded>Start game]
-.-> gameplay>Gameplay]
-.-> finished{Finished?} -.-> |No\nstay in loop| gameplay
finished -.-> |Yes| cleanup>cleanup]
cleanup
end
flowchart TD
init>Initialize the game]
init --> mode{Game mode?} --> |CUSTOM| menu_custom
mode --> |else| io_level
subgraph menu_custom[menu_custom.c]
menu_custom_open
end
subgraph io_level[io_level.c]
io_level_fullpath
end
subgraph game[game.c]
game_init
end
subgraph io_map[io_map.c]
map_open
map_load
map_load_stats
end
menu_custom_open --> game
io_level_fullpath --> game
game_init --> |Success| io_map
game_init --> |Init failed| main_menu>main menu]
map_open --> map_load
map_open --> map_load_stats
flowchart TD
start>Start game]
--> save{Found save?\nAsk: Load?}
--> |Yes| io_map
-.-> printer
save -.-> |No| printer
subgraph printer[printer.c]
direction LR
pall[print_all]
pall --> print_meta
pall --> print_map_all
pall --> height{"Height\n<12?"} --> |Yes| print_controls
end
subgraph io_map[io_map.c]
save_load[save load]
end
One of main.c
's primary functions is to handle the gameplay loop:
game.c :: game_wait_input()
is called continuously in a loop until the game
ends or the user exits the level. This function handles waiting for inputs and
calling the logic functions related to the input.
flowchart TD
gameplay>Gameplay] --> loop{Game loop} --> |input loop| game
subgraph game[game.c]
direction LR
game_wait_input
game_mv
goal
game_undo
game_reset
exit
end
game_wait_input --> |Directional move| game_mv -.-> goal{Goal met?}
game_wait_input --> |Undo| game_undo -.-> goal
game_wait_input --> |Reset| game_reset -.-> goal
game_wait_input --> |Exit| exit
goal -.-> |No| game_wait_input
goal --> |Yes| exit([Exit loop])
flowchart TD
cleanup>Cleanup]
-.-> game_won{Game lost?\nAsk: Save?}
--> |Yes| io_map -.-> game_e["game_end"]
game_won -.-> |Game won| game
subgraph io_map[io_map.c]
map_save_moves
end
subgraph game[game.c]
fame>Process fame] -.-> game_e
end
game_e -.-> main
subgraph main[main.c]
direction LR
ask_continue{Ask: Next/Retry\nor main menu?}
-.-> |Next/Retry| stay[Stay in gameplay loop]
-.- game_master>game_master]
ask_continue -.-> |Main menu| exit[Exit gameplay loop]
-.- menu>main_menu]
end
flowchart TD
fame>Process fame] --> full
subgraph data[data.c]
insert_fame_at
end
subgraph io_map[io_map.c]
map_save_stats
end
subgraph game["game.c - fame_add()"]
full{List not full?\nor\nIn TOP10?}
--> |Yes| ask{Ask: Add to list?}
ask --> |No| re(["return;"])
full --> |No| re
end
ask --> |Yes| data -.-> io_map
The following section was generated with docblox2md from the header files.
Brief: Read long string from stdin
Returns: read
— text
Brief: Create a new map instance with default values
Parameters:
loc
— Default value for map_data.locwidth
— Width of the map for allocationheight
— Height of the map for allocation
Returns: Created
— and memory allocated map_data pointer
Brief: Insert a new move at the end of the current chain
Parameters:
prev
— Any point of the current move chain
Returns: The
— new element
Brief: Insert new fame list item after a given one
Parameters:
prev
— Element to insert after
Returns: The
— new element
Brief: Insert new fame list item after the n.th one
Parameters:
first
— List item to count fromIndex
— to insert atname
— List item name valuemove
— List item move value
Returns: First
— item
Brief: Initialize and begin game on the given level
Parameters:
level
— Path to the XSB file for the map
Returns: generated
— map data
Brief: Ends the game and calls map_close to free all allocated memory
Parameters:
map
— Pointer to map data
Brief: Undo the last non-undo move made
Note: Undo counts as a regular move and increases the move counter
Parameters:
map
— Pointer to map data
Brief: Wait for and handle input
Returns: false
— if game is completed or aborted, true otherwise
Brief: Attempt to move the player
Parameters:
ud
— Indicate if movement is on the Y axis (Up-Down)rd
— true: Right or Left movement
Brief: Get the number of xsb files in the directory
Note: This function does not validate if the XSB files are valid
Parameters:
Directory
—directory
— to search in
Returns: The number of xsb files
Brief: Get the full path based on config directory
Parameters:
id
— ID of the map file
Returns: full
— (relative) path to the XSB
Brief: Get the file name of the map with a specific id
Parameters:
directory
— Directory to search inid
— Number of the map file
Returns: File
— name
Brief: Get the xsb metadata file's name, which is the file name with .dat/.sav at the end
Parameters:
loc
— Location of the XSB filestat
— true = stats file // false = save file
Returns: Malloced
— string
Brief: Open stats/save state file, which is just the xsb map file with .dat/.sav at the end
Parameters:
loc
— Location of the XSB filemode
— File open modestat
— true = stats file // false = save file
Returns: File
— pointer to the stats file
Brief: Read undefined length line
Parameters:
fptr
— File pointer to read from
Returns: Malloced
— string
Brief: Load the stats (leaderboard) of the map from the appropriate data file
Parameters:
map
— Pointer to map data
Returns: Success
— or Failure to load.
Brief: Save the moves of the map to the appropriate data file
Parameters:
map
— Pointer to map data
Returns: Success
— or Failure to save.
Brief: Load the moves of the map from the data file
Parameters:
map
— Pointer to map datasavptr
— FILE* to save file
Brief: Save the stats of the map into the appropriate data file
Parameters:
map
— Pointer to map data
Returns: Success
— or Failure to save.
Brief: Check if given metadata value is in the file at the expected location
Parameters:
meta
— Metadata namefptr
— XSB file pointer
Returns: true/false
— based on result
Brief: Opens the given map file
Parameters:
loc
— relative or absolute path to the xsb map file
Returns: generated
— map data
Brief: Loads the contents of the map. Must always be called after map_open()
Parameters:
map
— Pointer to map datamapptr
— file pointer to map file
Returns: Success
— or Failure to load map data.
Brief: Lazily (only doing what's necessary) resets the map from the map file
Parameters:
map
— Pointer to map data
Brief: Closes the map file and frees all allocated memory for the map
Parameters:
map
— Pointer to map data
Brief: Open the custom map input menu
Returns: The
— user provided (!) path
Brief: Highlight level menu item
Parameters:
id
— ID of the menu item (location on page)page
— Current page in view (for number calculation)max
— Maximum number of available levels (any more is greyed out)active
— State to highlight
Brief: Move level selection
Parameters:
loc
— Current locationpage
— Current pagemax
— Maximum number of available levels
Returns: Selection
— complete: false (to break out of loop)
Brief: Open level select menu and wait for completed selection
Returns: Selected
— level's number
Brief: Print navigation block in level select menu
Parameters:
direction
— ture: Right // false: Leftcolor
— (econio) color to print with
Brief: Print a single item on the level screen
Parameters:
num
— Number to showcolor
— (econio) color to print withid
— Sequential ID of the button
Brief: Print the full page of levels (loop through menu_print_level_item)
Parameters:
page
— Page no. to printmax
— Maximum number of levels, any more is greyed out 00
Brief: Print the full level selection screen
Parameters:
page
— Page to start printing atmax
— Maximum number of levels, any more is greyed out 00
Returns: Success/Failure
Brief: Main menu movement looper
Parameters:
type_loc
— Current selection
Returns: Selection
— complete: false (to break out of loop)
Brief: Open main menu and wait for game type selection
Returns: Selected
— game type or exit request
Brief: Print a single item on the main menu
Parameters:
id
— ID of the menu item (location)highlighted
— Highlight selection active/inactive
Brief: Print the full main menu
Brief: Print a given number of characters at a location
Parameters:
x
— X (Horizontal) locationy
— Y (Vertical) locationn
— Number of times to print characterc
— Character to print
Brief: Print a character at location
Parameters:
x
— X (Horizontal) location)y
— Y (Vertical) locationc
— Character to print
Brief: Prints the full game to the screen
Brief: Prints every map component to the screen
does not offset the print, prints wherever we're currently at Brief: Prints the current value to the screen at the given location of the map,
Parameters:
x
— Map location xy
— Map location y
with the appropriate amount of offset Brief: Prints the current value to the screen at the given location of the map,
Parameters:
x
— Map location of xy
— Map location of y
Brief: Print the logo and available map metadata (title, author, record)
Parameters:
map
— Pointer to map data
Brief: Update the number of moves displayed
Parameters:
new
— New number to show
Brief: Prints the control hints to the screen at current cursor position
Brief: Print the leaderboard of the map
Parameters:
map
— Pointer to map data