I have a class which contains some basic information as well as other classes, when saving this class
the attribute buildings gives error upon loading
TypeError: Building.__init__() missing 2 required positional arguments: 'name' and 'jobs'
without the saving and loading function it works perfectly fine.
the code for the classes is-
class Kingdom:
def __init__(self, total_water=100_000, water_add=667) -> None:
self.people = []
self.unemployed_people = []
self.buildings = []
self.resources = []
self.total_water = total_water
self.water_added = water_add
def add_building(self, name, x, y):
"this function will return -1 if the kingdon does not have the required resouces"
for value in building_info[name]["cost"]:
for i, item in enumerate(self.resources):
new_item = item - value
if not isinstance(new_item, int):
self.resources[i] = new_item
break
else:
return -1
width, height = assets[name].get_size()
self.buildings.append(Building(x, y, width, height, name, building_info[name]["jobs"]))
def display(self, window, x_offset=0, y_offset=0, resource_display_y_offset=0):
for building in self.buildings:
building.display(window, x_offset, y_offset)
# items
for i, item in enumerate(self.resources):
blit_text(
window,
item.name + " " + str(item.count),
(
0,
i * text_size + text_size * 0.2 * i - 5 + resource_display_y_offset,
),
size=text_size,
).get_width()
try:
window.blit(
assets[item.name],
(
resource_div_rect.x - default_item_size * 2,
i * text_size + text_size * 0.2 * i + resource_display_y_offset,
),
)
except KeyError:
window.blit(
assets["Missing Item"],
(
resource_div_rect.x - default_item_size * 2,
i * text_size + text_size * 0.2 * i + resource_display_y_offset,
),
)
def tick(self):
self.total_water += self.water_added
for person in self.people:
remaining_resources = person.job.work(self.resources)
if not isinstance(remaining_resources, int):
for i, item in enumerate(remaining_resources):
if item.name == "water" and self.total_water < item.count:
remaining_resources.pop(i)
break
if item.name == "water":
self.total_water -= item.count
break
self.resources = remaining_resources
for i, item in enumerate(self.resources):
if item.name == "person":
for j in range(item.count):
self.unemployed_people.append(Person())
self.resources.pop(i)
def employ_all_people(self):
for i, person in enumerate(self.unemployed_people):
for building in self.buildings:
if building.jobs > 0:
self.unemployed_people.pop(i)
self.people.append(Person(building))
building.jobs -= 1
break
class Person:
def __init__(self, job=None) -> None:
self.job = job
class Item:
def __init__(self, name, count, tag=None) -> None:
self.name = name
self.count = count
self.tag = tag
def __add__(self, other):
"""This function will return -1 if it cannot add the values"""
if isinstance(other, int) or isinstance(other, float):
return Item(self.name, self.count + other, self.tag)
if not isinstance(other, Item):
return -1
if self.name == other.name or (
(self.tag == other.tag or self.tag == other.name or self.name == other.tag)
and self.tag is not None
):
return Item(self.name, self.count + other.count, self.tag)
return -1
def __sub__(self, other):
"""This function will return -1 if it cannot subtract the values"""
if (
isinstance(other, int) or isinstance(other, float)
) and self.count > other.count:
return Item(self.name, self.count - other, self.tag)
if not isinstance(other, Item):
return -1
if (
self.name == other.name
or (
(
self.tag == other.tag
or self.tag == other.name
or self.name == other.tag
)
and self.tag is not None
)
) and self.count > other.count:
return Item(self.name, self.count - other.count, self.tag)
return -1
def __repr__(self) -> str:
return (
"{name: "
+ str(self.name)
+ ", count: "
+ str(self.count)
+ ", tag: "
+ str(self.tag)
+ "}"
)
class Building(pg.Rect):
def __init__(self, x, y, width, height, name, jobs):
super().__init__(x, y, width, height)
self.name = name
self.jobs = jobs
self.manual = True
def display(self, window, x_offset, y_offset):
if self.x - x_offset < resource_div_rect.x:
return
if self.bottom - y_offset > div_rect.y:
return
window.blit(assets[self.name], (self.x - x_offset, self.y - y_offset))
def work(self, resources, called_as_click=False):
"this function will return -1 if there are insufficent resoureces"
if self.manual and not called_as_click:
return -1
for item in building_info[self.name]["in"]:
for i, value in enumerate(resources):
new_item = value - item
if not isinstance(new_item, int):
resources[i] = new_item
break
else:
return -1
for item in building_info[self.name]["out"]:
for i, value in enumerate(resources):
new_item = value + item
if not isinstance(new_item, int):
resources[i] = new_item
break
else:
resources.append(item)
return resources
The full code repository for anyone interested is - https://github.com/IGR2020/PaperCiv Note it doesn't have a bugged code
I tried to make the name and job arguments default, and it loaded it but now the arguments were defaulted so I could not change the building data. I save the data using file = open and data = pickle.load method, it works on other types of data just fine.
Also if there is some other way to save a list of the kingdom class with all the other classes which are a part of it to memory please suggest it.
The saving and loading code is -
save_name = "main.pkl"
# loading world data / creating new world
if isfile(f"kingdom data/{save_name}"):
file = open(f"kingdom data/{save_name}", "rb")
kingdoms = pickle.load(file)
file.close()
main_kingdom = kingdoms["Main"]
else:
kingdoms = {"Main": Kingdom()}
main_kingdom = kingdoms["Main"]
main_kingdom.resources = [
Item("wheat", 100, "food"),
Item("water", 100),
Item("wood", 50),
Item("person", 1),
]
then the saving code is in the quit condition of the game loop -
for event in pg.event.get():
if event.type == pg.QUIT:
# saving current world data
file = open(f"kingdom data/{save_name}", "wb")
pickle.dump(kingdoms, file)
file.close()
run = False
The error comes after I open the app then place anyone building (i.e the well) then I quit and reopen the app and the error comes into play. The full code - https://github.com/IGR2020/PaperCiv/tree/Saving-and-Loading This does have the bugged code.
Here is the MRE -
import pygame as pg
import pickle
class Kingdom:
def __init__(self, total_water=100_000, water_add=667) -> None:
self.people = []
self.unemployed_people = []
self.buildings = []
self.resources = []
self.total_water = total_water
self.water_added = water_add
class Person:
def __init__(self, job=None) -> None:
self.job = job
class Item:
def __init__(self, name, count, tag=None) -> None:
self.name = name
self.count = count
self.tag = tag
class Building(pg.Rect):
def __init__(self, x, y, width, height, name, jobs):
super().__init__(x, y, width, height)
self.name = name
self.jobs = jobs
self.manual = True
kingdom = Kingdom()
kingdom.buildings.append(Building(0, 0, 0, 0, 0, 0))
file = open("main.pkl", "wb")
pickle.dump(kingdom, file)
file.close()
file = open("main.pkl", "rb")
kingdom = pickle.load(file)
file.close()
My computer model is DELL Inspiron Intel Core i5 7th Gen 7200U, This code is running outside of a virtual environment, using pygame 2.5.2 and python 3.12.2 on windows 10. I hope a solution can be found using this MRE.
You can't inherit from pygame.Rect
- it has some custom pickling behaviour that interferes with your adding extra arguments to the __init__
function. Maybe you could instead store a pygame.Rect
inside your Building
?
class Building:
def __init__(self, x, y, width, height, name, jobs):
self.rect = pg.Rect(x, y, width, height)
self.name = name
self.jobs = jobs
self.manual = True