• Tutorials
  • CSV to Dictionaries: Proper Database for your game!

Hello! &nbsp;I am very excited to share with you a bit of code today. &nbsp;This code is a direct translation of Pananag's tutorial on how to make a database for your game in Blender Game Engine. &nbsp;This is a very useful piece of code for which my games rely on, and now that I am turning my Blender Game Engine prototype into Godot for its build and distribution capabilities it was necessary that I translate it over.<br><br>I have taken Pananag's Python code and turned it into Godot Script for your database convenience.<br><br>Before anything further, here are links to Pananag in order to credit him for this excellent insight on how to organize your databases into dictionaries. &nbsp;Thank you Pananag for sharing your code.<br><br>Blender Artists:<br>https://blenderartists.org/forum/showthread.php?302504-Series-Basic-inventory-system-for-the-Blender-Game-Engine<br><br>Youtube main channel:<br><a rel="nofollow" target="_blank"><img width="640" alt="" height="385"></a><br><br>Creating the database:<br><a rel="nofollow" target="_blank"><img width="640" alt="" height="385" title="Image: null"></a><br><br>-------------------------------------<br>This code can be used for:<br>Taking your CSV files and making dictionaries within the Godot Engine that are nicely organized<br>-Making databases that have header attributes with corresponding values<br>-Magic databases<br>-Item databases<br>-Character databases<br>-Enemy databases<br><br><br><br>CSV example:<br>[ID, Name, Birthday, Favorite Weapon, Strength Stat]<br>[0001, Henry, July3rd, Swords, 1400]<br><br>What this code does:<br>The following code takes a .CSV (coma separated value file) and reads each line into an array. &nbsp;The first (top) line in the .CSV&nbsp;is read as an array of what will be used as the HEADERS I.E. ATTRIBUTES of each COLUMN of the .CSV file.<br><br>Example:<br><br>These values are now a dictionary. &nbsp;The definitions are shown on the next line as '' (&nbsp;empty strings )<br>{ID : '', Name : '', Birthday : '', Favorite Weapon : '', Strength Stat : ''}<br><br>The above dictionary, made from the top line of the .CSV file, will then be added as the definition of each key in a new dictionary made up of the first value of each subsequent line in the .CSV file<br><br>{0001 :&nbsp;{ID : 0001, Name : Henry, Birthday : July3rd, Favorite Weapon : Swords, Strength Stat : 1400}&nbsp;}<br>{0002 :&nbsp;{ID : 0002, Name : Billydilly, Birthday : December40th, Favorite Weapon : Sponges, Strength Stat : -30}&nbsp;}<br>ETC.<br><br><br><img alt="CSV EXAMPLEpng"><br><br>USAGE:<br><br>print(db_characters[CHAR_ID]['Name'])<br>print(db_items[ITEM_ID]['Price'])<br>print(db_monsters[MONSTER_ID]['Drop_1'])<br><br>EDIT:<br>***********************************************************************************************************<br>So we all want to write:<br><br>for key in database.keys():<br>print(key.keys())<br><br>But this does not work. &nbsp;The loop recognizes the keys as only strings, and not as the dicts which they represent.<br><br>In order to loop through the keys of keys like we would like, <br>you have to save each database.csv's first line (header) as a list. &nbsp;This requires editing the code below, which I will do with comments. &nbsp;This will also require adding an extra argument to the func.<br><br>Example:<br><br>

for item in database_1_headers:

node.set_meta(item,database_1[key][item])

print(node.get_meta(item)

<br><br>COMMON ERROR:<br><br>Using different programs to open up the save .csv file can cause formatting differences which will cause problems with how .csv files are read by programs trying to call them with read/write related methods.<br><br>Notably: Sometimes an extra line is added at the end of a .csv file after editing. &nbsp;This will cause the code to give an error.&nbsp;<br><br>I use Notepad++ and OpenOffice's database program to do my .csv files, and in using the two the extra line is added, so I have to open up Notepad++, delete the extra line and save before running the program. &nbsp;Of course, this only happens when editing your .CSV and not just randomly.<br><br><br><br>********************************************************THE CODE****************************************

<br>WITHOUT INDENTS or comments<br><br>

func db_import(csv_file,dict_to_append):#EDITED: In order to loop through keys of keys, EXTRA ARG<br> var file = File.new()<br> file.open(csv_file,file.READ)<br> var pos = 0 #artifact<br> var file_len = file.get_as_text().length() #artifact<br> var is_header = true<br> var &nbsp;attributes_list = &nbsp;[]<br> file.seek(0)<br> var temp_dict = {}<br> while !file.eof_reached(): <br> var line = file.get_csv_line()<br> if is_header == true:<br> attributes_list = line<br>for item in attributes_list: #EDITED: In order to loop through keys of keys<br>DICT_HEADERS_LIST.append(item) #EDITED: In order to loop through keys of keys<br> is_header = false<br> else:<br> for i in range(0,attributes_list.size()):<br> temp_dict[attributes_list[i]] = line[i]<br>dict_to_append[line[0]] = temp_dict<br>temp_dict = {} #Edited as of 8/26/2016<br> print(dict_to_append)

This is the code with comments and tabs shown:<br><br><br><br>

func db_import(csv_file,dict_to_append,DICT_HEADERS_LIST):#EDITED: In order to loop through keys of keys as noted in the EDITED section of the first post, EXTRA ARG is called DICT_HEADERS_LIST

tab-&gt;var file = File.new()<br>&nbsp;#creates a new Class instance. &nbsp;A var will store this reference as 'file'. (file is not a string)

tab-&gt;file.open(csv_file,file.READ) <br>#the file is read into memory. &nbsp;The .csv can now be manipulated with File methods.

<br>

tab-&gt;var is_header = true&nbsp;<br>#this bool will toggle to false once the while loop has run once.

<br>tab-&gt;var &nbsp;attributes_list = &nbsp;[]&nbsp;<br>#this list will store the first item of each column of the .csv

<br>tab-&gt;file.seek(0)&nbsp;<br>#this makes sure that the reader's cursor position (an invisible cursor) is at the first character.

<br>tab-&gt;var temp_dict = {}&nbsp;<br>#a temporary dictionary which will only live within a call of this func

<br>tab-&gt;while !file.eof_reached():&nbsp;<br>#this says 'While the reader's invisible cursor is not on the last character in the .csv

<br>tab-&gt;tab-&gt;var line = file.get_csv_line()&nbsp;<br>#a variable called line will store an array of all of the Comma Separated Strings on one line of the .csv file

<br>tab-&gt;tab-&gt;if is_header == true:&nbsp;<br>#Is the bool is_header true?

<br>tab-&gt;tab-&gt;tab-&gt;attributes_list = line&nbsp;<br>#yes, it is, so set the pre-made array variable above the while loop to equal the first line (which is also an array)<br><br>tab-&gt;tab-&gt;tab-&gt;for item in attributes_list:<br>#EDITED: In order to loop through keys of keys #for each item in that array variable,<br><br>tab-&gt;tab-&gt;tab-&gt;tab-&gt;DICT_HEADERS_LIST.append(item)<br>#EDITED: In order to loop through keys of keys #append the list-as-arg with whatever column header is being iterated over.<br><br>tab-&gt;tab-&gt;tab-&gt;is_header = false&nbsp;<br>#the above loop will not continue because now is_header is false. &nbsp;You now have a list of header attributes which will be used as keys to locate the specific values entered below them in the .csv file.<br>

<br>tab-&gt;tab-&gt;else:&nbsp;<br>#the header list has been created and is_header is false, so continue below

<br>tab-&gt;tab-&gt;tab-&gt;for i in range(0,attributes_list.size()):&nbsp;<br>#for each index, from the smallest to largest possible value in the array

<br>tab-&gt;tab-&gt;tab-&gt;tab-&gt;temp_dict[attributes_list[i]] = line[i]<br>#create a key in the temp_dict variable that is named some item of the attribute_list i.e. the list of headers.<br>#which header is used is notated by the index. &nbsp;<br>#this newly created key will be defined i.e. have the value of an item in an array of strings i.e. the array of strings taken from a line in the .csv file.

<br>tab-&gt;tab-&gt;tab-&gt;dict_to_append[line[0]] = temp_dict&nbsp;<br>#our permanent dictionary which we want to create will have&nbsp;a&nbsp;key&nbsp;that&nbsp;is&nbsp;named to be&nbsp;the first item&nbsp;in every line but the first's&nbsp;first item

tab-&gt;tab-&gt;tab-&gt;temp_dict = {}<br>&nbsp;#Edited as of 8/26/2016 , temp dict has to be cleared or it will only contain the last value&nbsp;<br>tab-&gt;print(dict_to_append)

Very nice work. I will be working with this soon. Thanks alot!!

a month later

As a noob, it's super hard to read.

Can you write you code within

code

?

13 days later

Hi. I'm new to Godot. This script is a great help! Thanks to 1000h and Pananag's. I also had trouble understanding without indenting. This is retouched to the forum code label version. `func db_import(csv_file,dict_to_append,DICT_HEADERS_LIST): #EDITED: In order to loop through keys of keys as noted in the #EDITED section of the first post, EXTRA ARG is called DICT_HEADERS_LIST var file = File.new() #creates a new Class instance.

A var will store this reference as 'file'. (file is not a string)

file.open(csv_file,file.READ) #the file is read into memory.

The .csv can now be manipulated with File methods.

var is_header = true #this bool will toggle to false once the while loop has run once. var attributes_list = [] #this list will store the first item of each column of the .csv file.seek(0) #this makes sure that the reader's cursor position is at the first character. var temp_dict = {} #a temporary dictionary which will only live within a call of this func. while !file.eof_reached(): #this says 'While the reader's cursor is not on the last character in the .csv var line = file.get_csv_line() #a variable called line will store an array of all of

the Comma Separated Strings on one line of the .csv file

if is_header == true: #Is the bool is_header true? attributes_list = line #yes, it is, so set the pre-made array variable above the while loop

to equal the first line (which is also an array)

for item in attributes_list: #EDITED: In order to loop through keys of keys #for each item in that array variable, DICT_HEADERS_LIST.append(item) #EDITED: In order to loop through keys of keys #append the

list-as-arg with whatever column header is being iterated over.

is_header = false #the above loop will not continue because now is_header is false.

You now have a list of header attributes which will be used as keys to

locate the specific values entered below them in the .csv file.

else: #the header list has been created and is_header is false, so continue below for i in range(0,attributes_list.size()): #for each index, from the smallest to largest possible value in the array temp_dict[attributes_list[i]] = line[i] #create a key in the temp_dict variable that is named some item

of the attribute_list i.e. the list of headers.

#which header is used is notated by the index. #this created key will be defined i.e. have the value of an item in an array of strings

i.e. the array of strings taken from a line in the .csv file.

dict_to_append[line[0]] = temp_dict #our permanent dictionary which we want to create will have a key

that is named to be the first item in every line but the first's first item

temp_dict = {} #Edited as of 8/26/2016 , temp dict has to be cleared or it will only contain the last value print(dict_to_append)`