From e655de8f5458a76409e48b92498b243108a4e5db Mon Sep 17 00:00:00 2001 From: KuMiShi Date: Thu, 29 Jan 2026 16:55:45 +0100 Subject: [PATCH] Server & Game refactor basics --- .gitignore | 2 + __pycache__/dice.cpython-312.pyc | Bin 996 -> 0 bytes __pycache__/entity.cpython-312.pyc | Bin 1576 -> 0 bytes __pycache__/game.cpython-312.pyc | Bin 625 -> 0 bytes __pycache__/inventory.cpython-312.pyc | Bin 2799 -> 0 bytes __pycache__/item.cpython-312.pyc | Bin 2474 -> 0 bytes __pycache__/npc.cpython-312.pyc | Bin 682 -> 0 bytes __pycache__/player.cpython-312.pyc | Bin 890 -> 0 bytes __pycache__/serializable.cpython-312.pyc | Bin 4586 -> 0 bytes dice.py | 12 +++--- entities/entity.py | 22 ++++++++++ npc.py => entities/npc.py | 2 +- player.py => entities/player.py | 0 entity.py | 30 -------------- event.py | 26 ------------ events/event.py | 14 +++++++ events/turn.py | 14 +++++++ game.py | 41 +++++++++++++++++-- inventory.py | 39 ------------------ items/inventory.py | 37 +++++++++++++++++ item.py => items/item.py | 16 ++++---- main.py | 6 --- server.py | 49 ++++++++++++++++++++++- 23 files changed, 189 insertions(+), 121 deletions(-) delete mode 100644 __pycache__/dice.cpython-312.pyc delete mode 100644 __pycache__/entity.cpython-312.pyc delete mode 100644 __pycache__/game.cpython-312.pyc delete mode 100644 __pycache__/inventory.cpython-312.pyc delete mode 100644 __pycache__/item.cpython-312.pyc delete mode 100644 __pycache__/npc.cpython-312.pyc delete mode 100644 __pycache__/player.cpython-312.pyc delete mode 100644 __pycache__/serializable.cpython-312.pyc create mode 100644 entities/entity.py rename npc.py => entities/npc.py (60%) rename player.py => entities/player.py (100%) delete mode 100644 entity.py delete mode 100644 event.py create mode 100644 events/event.py create mode 100644 events/turn.py delete mode 100644 inventory.py create mode 100644 items/inventory.py rename item.py => items/item.py (70%) delete mode 100644 main.py diff --git a/.gitignore b/.gitignore index 3dc82e5..ad1ef2b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ .python-version __pycache__/ uv.lock + +# Save/Load files for testing *.json \ No newline at end of file diff --git a/__pycache__/dice.cpython-312.pyc b/__pycache__/dice.cpython-312.pyc deleted file mode 100644 index b836c8e6d50014bfa0016b1cbd7f0d00c0f69add..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 996 zcmah{J%|%Q6rS0?B38qXhCi!_48A%f(x=Dv}fs2c<7T8HS={A4RZGj(QH!I%;O*__l3 z?r;w^(?$ZtyM`QxYv=t$%5x$2qj$n(MqM zK|tvtc$~gA_EQb-5!b98Vlmf@Z54Sk0)A1qhbVfmX-FRDq}So_JrIcoRAwMUVOm*c z0b@&c!*H0>w9wS?^AH)6Z!3l8qodh+ zls;#|D35upE1Aw5u4-y0_ON$V0sxMM;`^? diff --git a/__pycache__/entity.cpython-312.pyc b/__pycache__/entity.cpython-312.pyc deleted file mode 100644 index 059185e5a1bebbe1748d829d47d710c61dda3e5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1576 zcmZ`(zi-<{6h4wiiuz^8aVsfC(zms=Cy0krAz;ct}Q?uP;}^&&4AP;Q{E#*H8I))_`UbNd++Yvy?6YrS}hS6 zzux-J`+^ekH`PA~Ly0TxCLB%9Nj#EY4C> zs|r?)(WO;Otp%{^Oj=qC!=h1ofq{l3D*KFkj_;i~M?N!TLCtUwh{ARlqteZm&rn*B z*$1GU5=Jb^C6??;mI5DjWlMDxOLM7Ja8*kmYDPh5&2H@VLZL@7XW^sxSX8_)X1?z| zVqupF?RzhBdx6lq#}4M;$hn;3f;CtCt$WMO12&J+<}jJ#kQXe9DgyR zH4hG&t*?Z7cki2)Db)Q3=FX0w`>keMXh6)?mqKmc+ukvslNsjla;tT#a~Lrmb-o?= z9_!rcbq4_pJj^7sh4qdx1CJHdJ24HCGT;0Yu6C`f&S!y?tQr ziq*q?({Amx_YZe>o40pNUPHI^5DnBea;Sh3AwULe7svOLTK(x>=7>IToEEdialX=; zD^IZ63Zu|{>O<5Y1CYP>)SV=BlpVHs; zOG)iA&~iSxwhpY2yuAUUl#44jrt}YeBQ3wsoa~KOj6>_L<=zAMQ{ZY zBr#Bsgph!#p#@7A0SZST*#sn4dff#?2A#z`Jv8LUU$}U zA%polQXi z*&}1c5ohL69Lvm{FTzq&5l&`7I%Q62i!-uwj*YnFHt2E7F2w^qZs_sOqnkLwhmq*5 zx<{s_l_?`b@>M<^y(z__*=S5P@-b$|Dd^ehw{;H0cesR%#^3@|L|+3Q4{JRbB~TGb zjnzdAt(xx$<+-9L?8-nIBN^|`%2tcA^&;8&}p$DPg z{byE#OTp5WyH_6h&#ZNvHC<=_0Zpf;|JFa?+64;jv_SbB6bJ_lj-n*!%_WGg?*kYZ zAO(}8E1F83L^4x=kpg%(ix+!XRK&nl$7mH5m=6SS+d`oYC}9vu5*;=SZMtNWC;)3~ zWU1ld(Nx{AM+<>%B({4gj`}GOw^3zJV7{#q*jEZIhc*#*zcU}K@ct#ER9r6J{qjCr z?mk)OPc95^IAH8)$KLsN(g_Qzo~SG`A=$ zN-BsD8G=12FPqBua1sBnwE|EDc4h9dVR{}{lR;=EX}mR+VuRI~df)n_j@KgqR#|FQ zXfd^ieOACpw=?lkWgq(C7>*F!e}Q1l-x-81 zuw>C;Xa1@8(QWmgK>*>rUF+W7vbT4|SnoSk?mM;Cce?C7{YzkdFjO84J@SSsdxcVF zIb(JmHIII3j!&$Q$I9a|GoD?DEL zo@eu*YdiJq=_6N@R&rwe=l+0@mP;THX-kXrMdfXr|SJR>Y}ea9q6TFEaFr3L_RC zb^D33^Rf+zos&sF7$X18$F788r{kTWQSUoQ@i<}QQBT8+yBE@MhyPA~A-^)R*3n<- z-v5nvgM{ro zJI3i<5K1#5P;mqVyt@V=Z&S@z`xl8Rng;IM79h@B?=GRa%@TAAeGtUUIrNNi zaN_Fa=Lo7z59S6poh;YA!E>C3SC4EVT5q;<+{bvM-N^-3{VxzymPgiJqas)3;diI; zpGat&S*Y4arB)vxdjWm^U%5WC{XIwCg(f|p-VFj?ER3I^{wJvGZ}u<_Eb-3~RMx_O E0g6;zo&W#< diff --git a/__pycache__/item.cpython-312.pyc b/__pycache__/item.cpython-312.pyc deleted file mode 100644 index 5b84d0b05f109a70cb6057987fd74fad0273b943..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2474 zcmcguO>7%Q6rR~1dmY{gc@> z5XXlcBHPQ3+^R#i{DH@kK>!GOepk^E-n&6}M! z-}~Mh|Cmhn5NPjz@R2no5%L>O+D#4yNAJL3ix|Y9HYw5^r9>ca5<^%ghG^1V(YMP9 zTq}WAHieE>mqam%dqNuS4xKX8E;+Dy*ylvI;3%05IU-M-wm>j(ZgHOQ;ljNvD za)J>xs3{r({6zRkrc_WGq(BXMJ(-gYI4&1ykWXkXdW)!vC%xsGMNcZ_i>4PhOh;!{ z#kI;MFX^~>S1Xnct6-VTi+pTaiyyjr`|5Z+UJrjqIS^mbE#gA&w+*>Y?ttxXFsGzW z>qHaU>vaKU0X&4%MVJA|eyd9`>zuPfc4$_Oc(PNifFFvcStZNWG!_AC7Db_;KtRAu za!k9>ZOw~mnr`PEN7EeKn!&dAWPJMS{CkecocX1yZJF~kWxZN7ORh6t)GL?gEkI|Y zvci(Eqn1RhCfW|5zs}1b9Q2z}u3|2buJzatofO4q=UU=nO8A2>(YeMEiEt0vt zs9w#eqK^%t#lhR5>qS=_$z{n*cwQ~Q%sdzYs7dS~{O8TJeq@6bh%(8g{6*&ujGG)?n*G^AG5#&JT^ zmaBO?SP|hgPcGQyyvtE%1F+jm2Eo%!+txI2x)-$E=^NZ>hA?u0SUML$JYFZi3$i@i zynINY+n(m#J%@Kcy!sH8kIqnemU2(|W*zo*S{u`&Nl=|gF&R^=An>#jAgv@rgtU?k z$na2dMZBKb<*HSwVDfs3$;YOlBm3G7$2|I&#-~y!;-?TKjsOWF?*{%rDq(_jxUfPP z#s(srfX>J4JSw6*vZr$amw2-H5Ml!HRLHFPKys#^ed>6J?;P=AFc0y)InZ3)zIpG; zfil%fXMo^Ij^zo0qoe$a{V;%HiFE)*ep?}`kbM{8$=x~&$}kgUj#;fQ# z5hxI{7oRSdoNBSidD>H2(8>j^>cCs@KQL7VcmIZ|xb&|~9SHf}q3R&)bpC;=gJTf7 z-oH{6Y09aZg~1l-UW*NSdj=d!)!`BCR;vx61{BbRFv)@DVwPbhJitP$QpPWLntF=v z2g0nKxK>+>L3c($0PW+wcdu_;Z@&Fge594=yF2&UTyuFpk@;foP$Y@-K1nz$qo-LZ zTBV?}z5<`>&PS~C3+t}V-)3*|5tXK}%pyRIzBlfJUDAmz%Dh2YsvF;LBj zj|whm3e}FYf+tD5!?Td9#;aIH!&EupAP})k17!?yWd|Z13M!v-9;qf{VG>BmnAEy2 z{vfxUu(c9u(HD7%;CL6l zJDt6?ugQJHAJ&De`F zNa~wKC)HW%4QWi;35wIY8$AiQB{m3!ehmxIzd)WMIG(^?1MNN@fkts=#pt=FYS2Km tIicB$v=2}dbg>iXMy=(hI8UMOBF_>WnTYrUHh)_N_Ho+%1NvO#{R4Y8s+9l$ diff --git a/__pycache__/player.cpython-312.pyc b/__pycache__/player.cpython-312.pyc deleted file mode 100644 index 2b0a4b3e2fda1f27dbc063e808a507e91db00287..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 890 zcmYjP&ui3B5T2J`O}1IvgJ4%hs|7VZ^q@Bp5v+1(VWl{FJ{6_GtQFvY(ZE7Th16q`Gg%N8H=KDo+UmL#^W#zYOyf=1&@bm>b;clw7S@cLROuw`HdJCmsVrH zexX|Liau3ua{13Z87Y=9)t^TSm|jOXSb<%$ygsyJj%^;^Tz*q|d+Xhq_vg0n&Tij- zxJ`qPCflu?es!#+Jw5bwc;t&c`eJ5%=CeJqV^6%E{$xA*D%hhPWCCs?pmr#ll!8)G zqBO1rjY#@7rH>k3RAjoB66M}_K59d$L--gHq;eZqctsM*6?Q?Bi88{$D*RS7eewq& z@T2`au8hlns#ueOGN9~SR&~p_pg1Z?|CpDGJ6-0dFqGZWBMAE{A!H9u?m^|JrjyCm K>@UDqx9~4Ybk@`W diff --git a/__pycache__/serializable.cpython-312.pyc b/__pycache__/serializable.cpython-312.pyc deleted file mode 100644 index b53dcacebf7a1b6bf760975b119f085d598526e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4586 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 diff --git a/dice.py b/dice.py index 59cd6f7..eb68ce2 100644 --- a/dice.py +++ b/dice.py @@ -1,13 +1,13 @@ +# Native imports import random as rd class Dice(): - def __init__(self, num_faces:int=20): - self.num_faces = num_faces + @staticmethod + def roll(num_faces=20): + return rd.randrange(start=1, stop=num_faces+1, step=1) - def roll(self): - return rd.randrange(start=1, stop=self.num_faces+1, step=1) - - def head_or_tails(self): + @staticmethod + def head_or_tails(): result = rd.randint(0,1) if result: # true return "head" # face diff --git a/entities/entity.py b/entities/entity.py new file mode 100644 index 0000000..6994a1c --- /dev/null +++ b/entities/entity.py @@ -0,0 +1,22 @@ +# Game imports +from serializable import Serializable + +# Native imports +import uuid + +class Entity(Serializable): + def __init__(self, name, strength, dexterity, intelligence, wisdom, charisma, hp, armor, speed, equipped_item=None): + self.id = str(uuid.uuid4()) + self.name = name + self.strength = strength + self.dexterity = dexterity + self.intelligence = intelligence + self.wisdom = wisdom + self.charisma = charisma + self.hp = hp + self.armor = armor + self.speed = speed + self.equipped_item = equipped_item + + def get_id(self): + return self.id \ No newline at end of file diff --git a/npc.py b/entities/npc.py similarity index 60% rename from npc.py rename to entities/npc.py index 8610fb7..5766dc8 100644 --- a/npc.py +++ b/entities/npc.py @@ -1,5 +1,5 @@ from entity import Entity class NPC(Entity): - def __init__(self, name, strength, dexterity, intelligence, wisdom, charisma, hp, armor, speed, ): + def __init__(self, name, strength, dexterity, intelligence, wisdom, charisma, hp, armor, speed): super().__init__(name, strength, dexterity, intelligence, wisdom, charisma, hp, armor, speed) diff --git a/player.py b/entities/player.py similarity index 100% rename from player.py rename to entities/player.py diff --git a/entity.py b/entity.py deleted file mode 100644 index 4058183..0000000 --- a/entity.py +++ /dev/null @@ -1,30 +0,0 @@ -from serializable import Serializable -from enum import Enum, IntEnum - -class Action(Enum): - ATTACK = 'strength' # Physical Battle action - FORCE = 'strength' # Actions that requires physical effort - SPELL = 'intelligence' # Many kind of spell (battle or not) - SCAN = 'wisdom' # Danger in environment or NPC's lies - SPEECH = 'charisma' # To persuade or deceive - AGILE = 'dexterity' # Avoid traps or incoming attacks & spell - -class BonusAction(IntEnum): - EQUIP_ITEM = 0 - USE_CONSUMMABLE = 1 - -class Entity(Serializable): - def __init__(self, name, strength, dexterity, intelligence, wisdom, charisma, hp, armor, speed, equipped_item=None): - self.name =name - self.strength = strength - self.dexterity = dexterity - self.intelligence = intelligence - self.wisdom = wisdom - self.charisma = charisma - self.hp =hp - self.armor = armor - self.speed = speed - self.equipped_item = equipped_item - - def make_a_turn(self, action:Action, bonus_action:BonusAction): - pass \ No newline at end of file diff --git a/event.py b/event.py deleted file mode 100644 index 58b067f..0000000 --- a/event.py +++ /dev/null @@ -1,26 +0,0 @@ -from serializable import Serializable -from entity import Action, BonusAction, Entity - -import json - -class Event(Serializable): - def __init__(self, location:str, entities_objectives:dict[str,str]): - super().__init__() - self.location = location - self.entities_objectives = entities_objectives - self.description = "" - - def add_turn_event(self, event_text:str, action:Action, bonus:BonusAction): - event_dict = {} - event_dict['text'] = event_text - event_dict['action'] = action - event_dict['bonus'] = bonus - self.description += json.dumps(event_dict) - - def update_objectives(self, entities:list[Entity], objectives:list[str]): - assert len(entities) == len(objectives), f"Number of entities & objectives are different: {len(entities)} (entities) and {len(objectives)} (objectives) !" - n = len(entities) - for i in range(n): - entity_name = entities[i].name - objective = objectives[i] - self.entities_objectives[entity_name] = objective \ No newline at end of file diff --git a/events/event.py b/events/event.py new file mode 100644 index 0000000..5785df0 --- /dev/null +++ b/events/event.py @@ -0,0 +1,14 @@ +# Game imports +from serializable import Serializable +from entities.entity import Entity + +# Native imports +import json +import uuid + +class Event(Serializable): + def __init__(self, location:str): + super().__init__() + self.id = str(uuid.uuid4()) + self.location = location + self.description = "" \ No newline at end of file diff --git a/events/turn.py b/events/turn.py new file mode 100644 index 0000000..9c5a3ae --- /dev/null +++ b/events/turn.py @@ -0,0 +1,14 @@ +# Native imports +from enum import Enum, IntEnum + +class Action(Enum): + ATTACK = 'strength' # Physical Battle action + FORCE = 'strength' # Actions that requires physical effort + SPELL = 'intelligence' # Many kind of spell (battle or not) + SCAN = 'wisdom' # Danger in environment or NPC's lies + SPEECH = 'charisma' # To persuade or deceive + AGILE = 'dexterity' # Avoid traps or incoming attacks & spell + +class BonusAction(IntEnum): + EQUIP_ITEM = 0 + USE_CONSUMMABLE = 1 \ No newline at end of file diff --git a/game.py b/game.py index 6913e4b..8505717 100644 --- a/game.py +++ b/game.py @@ -1,10 +1,43 @@ from serializable import Serializable from dice import Dice +from events.event import Event +from entities.player import Player +from entities.npc import NPC +from items.item import Item class Game(Serializable): def __init__(self, seed:int=42): - self.players = [] - self.npcs = [] - self.items = [] + self.active_players:list[Player] = [] + self.active_npcs:list[NPC] = [] + self.active_items:list[Item] = [] - \ No newline at end of file + self.events:list[Event] = [] + + def get_player(self, player_id:str): + for player in self.active_players: + if player.id == player_id: + return player + raise ReferenceError(f"The player #{player_id} doesn't exist!") + + def get_npc(self, npc_id:str): + for npc in self.active_npcs: + if npc.id == npc_id: + return npc + raise ReferenceError(f"The npc #{npc_id} doesn't exist!") + + def get_item(self, item_id:str): + for item in self.active_items: + if item.id == item_id: + return item + raise ReferenceError(f"The item #{item_id} doesn't exist!") + + def get_current_event(self): + idx = len(self.events) - 1 + if idx < 0: + raise IndexError("There is no event yet, you should create one!") + return self.events[idx] + + def add_event(self, new_event:Event): + self.events.append(new_event) + + #TODO: Add State Summary as Resource? \ No newline at end of file diff --git a/inventory.py b/inventory.py deleted file mode 100644 index 1f748d1..0000000 --- a/inventory.py +++ /dev/null @@ -1,39 +0,0 @@ -from serializable import Serializable -from item import Item - -class Inventory(Serializable): - def __init__(self, max_capacity:float = 50.0): - super().__init__() - self.items:list[Item] = [] - self.max_capacity = max_capacity # Weight (kg) - - def current_capacity(self): - return sum([item.weight for item in self.items]) - - def list_items(self): - s_items = '' - for item in self.items: - s_items += item.__str__() + '; ' - return s_items - - def add_item(self, added_item:Item): - if added_item.weight + self.current_capacity() > self.max_capacity: - return f'{added_item.name} is too heavy to fit inside the inventory: {added_item.weight + self.current_capacity() - self.max_capacity}kg in surplus!' - else: - self.items.append(added_item) - return f'{added_item.name} added to inventory. Current items load: {self.current_capacity()}kg' - - def remove_item(self, r_item): - if r_item in self.items: - self.items.remove(r_item) - return f'{r_item.name} was removed from the inventory' - else: - return f'{r_item.name} is not present within the inventory' - - def get_item(self, item_name:str): - for item in self.items: - if item.name == item_name: - return item - - # The item was not found - return None \ No newline at end of file diff --git a/items/inventory.py b/items/inventory.py new file mode 100644 index 0000000..41088f7 --- /dev/null +++ b/items/inventory.py @@ -0,0 +1,37 @@ +from serializable import Serializable +from items.item import Item + +class Inventory(Serializable): + def __init__(self, max_capacity:int = 5): + super().__init__() + self.items:list[Item] = [] + self.max_capacity = max_capacity # Maximum umber of items + + def current_capacity(self): + return len(self.items) + + def list_items(self): + s_items = '' + for item in self.items: + s_items += item.__str__() + '; ' + return s_items + + def add_item(self, added_item:Item): + if self.current_capacity() == self.max_capacity: + return f'The inventory is full!' + else: + self.items.append(added_item) + return f'{added_item.name} added to inventory. Current number of items: {self.current_capacity()}/{self.max_capacity}' + + def remove_item(self, item_id:str): + searched_item = self.get_item(item_id=item_id) + if searched_item: + return f'{searched_item.name} was removed from the inventory.' + raise ValueError(f'Item #{item_id} is not present within the inventory!') + + def get_item(self, item_id:str): + for item in self.items: + if item.id == item_id: + return item + # The item was not found + return None \ No newline at end of file diff --git a/item.py b/items/item.py similarity index 70% rename from item.py rename to items/item.py index 0888990..5202eae 100644 --- a/item.py +++ b/items/item.py @@ -1,19 +1,21 @@ from serializable import Serializable +import uuid + class Item(Serializable): - def __init__(self,name:str, description:str, stat_modifier:str, weight:float = 10.0): + def __init__(self,name:str, description:str, stat_modifier:str): super().__init__() + self.id = str(uuid.uuid4()) self.name = name self.description = description self.stat_modifier = stat_modifier - self.weight = weight def __str__(self): - return f"{self.name} ({self.weight} kg): {self.description}" + return f"{self.name}: {self.description}" class Equippable(Item): - def __init__(self, name, description, stat_modifier, equipped:bool, weight = 10.0): - super().__init__(name, description, stat_modifier, weight) + def __init__(self, name, description, stat_modifier, equipped:bool): + super().__init__(name, description, stat_modifier) self.equipped = equipped def equip(self): @@ -23,8 +25,8 @@ class Equippable(Item): self.equipped = False class Consummable(Item): - def __init__(self, name, description, stat_modifier, nb_of_uses:int, weight = 10.0): - super().__init__(name, description, stat_modifier, weight) + def __init__(self, name, description, stat_modifier, nb_of_uses:int): + super().__init__(name, description, stat_modifier) self.nb_of_uses = nb_of_uses def consumme(self): diff --git a/main.py b/main.py deleted file mode 100644 index 5c5d443..0000000 --- a/main.py +++ /dev/null @@ -1,6 +0,0 @@ -def main(): - print("Hello from mcp-nlp!") - - -if __name__ == "__main__": - main() diff --git a/server.py b/server.py index e1d4da4..5f56ef0 100644 --- a/server.py +++ b/server.py @@ -11,7 +11,7 @@ from serializable import Serializable import json import os mcp = FastMCP("wyvern-castle") -game = Game() +game: Game = None logging.basicConfig( level=logging.INFO, @@ -21,8 +21,53 @@ logging.basicConfig( ] ) -# History file path +# Constants HISTORY_FILE = "game_history.json" +SAVE_PATH = "save_" + +@mcp.tool() +async def load_game(slot:int): + """Loads an already existing game. + + Args: + slot: Integer id of the save slot. + """ + global game + path = SAVE_PATH + str(slot) + ".json" + try: + with open(path, "r", encoding="utf-8") as f: + game.deserialize(f.read()) + return { + "success": True, + "msg": f"{path} as been successfully loaded!" + } + except OSError as e: + return { + "success": False, + "error": str(e) + } + +@mcp.tool() +async def save_game(slot:int): + """Saves the current game to the given slot. + + Args: + slot: Integer id of the save slot. + """ + global game + path = SAVE_PATH + str(slot) + ".json" + try: + with open(path, "w", encoding="utf-8") as f: + f.write(game.serialize()) + return { + "success": True, + "msg": f"{path} as been successfully saved!" + } + except OSError as e: + return { + "success": False, + "error": str(e) + } def append_to_history(event: Dict[str, Any]): """Append a game event to the history file."""