I want to preface this by saying that I think I got cooked by AI here.
I have a game where I am working on a job system. The job system has a job objects which I need to put into a data structure that can be searched via id, location, or work type. At first I was building with the mindset of "do whatever to solve this problem and make it work" but it became very hard to implement new features because I would break everything. So I started trying to think ahead and design ways of doing things that would handle all the different jobs and things.
At first I put all jobs into an array and iterated through. Then I learned about dictionaries and started using them for way to many things and so i updated my job queue to be a dictionary with the Key as ID > JobObj
Then I decided to plan out the job system before writing anything and "do it properly". I decided I needed to upgrade the data structure holding jobs because its a core part of the game and will be heavily interacted with. But I realized I only know array, dictionary and database, so I asked AI what data structure I should use and it suggested a nested dictionary.
Now im using multiple dictionaries but im really hating it. Its hard for me to work with and conceptually im not sure I can visualize how its even working.
How I am thinking about it is there is multiple layers of keys, 1st layer is work types, then once I find the work type it points to a dictionary of region IDs and that points to an array of jobs in the region.
Job def is work type like Planting Region id: the map is broken down into region IDs so i dont have to check every tile and can limit seaching
#python
var job_pool: Dictionary = {}
func register(designation: DesignationObj) -> void:
var job_def = designation.job
var region_id = designation.region_id
if not job_pool.has(job_def):
job_pool[job_def] = {}
if not job_pool[job_def].has(region_id):
job_pool[job_def][region_id] = []
job_pool[job_def][region_id].append(designation)
Here is how I am picturing it in my head.
var job_pool {
"plants": {
"1": {
"job1"
"job2"
}
"2": {
"job3"
}
}
"mining": {
"1": {
"job4"
"job5"
}
}
"hunting": {
"5":{
"job12"
}
}
}
But now I want to add ID look up into this im stuck and im thinking the entire structure does not work for what it needs to do.
One important thing to think through is how your resulting data structure will be used and how each layer and its overhead fits in.
Like for example, I once used a data structure that ended up being an array of maps to store a series of chunks of arbitrary data. It would parse each line of data into key/value pairs in a map/dictionary and then add that as an entry for the array. It was slow.
It was much faster and used less RAM when I changed it to a map of arrays, since each line would have the exact same pieces of data in a file, it just wasn't certain what would be in any given file. This meant that there was only one place where all the map overhead was stored, and each entry in the arrays corresponded to the same entry in the other arrays.
I'd also suggest a design where you don't care what the actual implementation is outside of the module that needs to do the storing itself. The main thing you need is a unique addressing system to ensure that a) each piece of data has a predictable label/address to find it and b) each label/address only points at the relevant data.
Eg, if you end up using an SQL database, you don't want to be writing SQL queries into your crafting system, but to use identifiers that you pass into the storage module where it builds the queries (or even better, uses stored procedures so the queries themselves are also stored on the database, which is faster and more secure). Then, you can decide later on down the line that instead of an SQL database, maybe you should use the new DreamStorage that uses the brainpower of those foolish enough to be early adopters of brain chip technology to do table lookups in constant time as long as it doesn't involve math. If you do modularization correctly, you should just be able to swap them out by matching the interface, regardless of what the implementation is.
If there's a hierarchy built into the way the data is organized, try to abstract it (eg something like "level0.level1.level2.item", where you can split the levels by "." when you need to). You should be designing the data structures for what your algorithms need to do with the data, not based on how you intend to store that data. Then the control flow side extracts the data each function needs and provides it to the function and then handles the result accordingly.
That said, don't worry too much about getting it perfect. Experience is going to be your biggest ally, so go ahead and make mistakes and learn from them. There isn't likely a solution that will trivialize everything. I usually just aim for "have a good idea of what all the blocks need to do, ideas on how they will do it, and how they will fit together" with my designs.