Efficient way to modify a .json file - python

I am trying to create a textgame in Python, and it is relying pretty heavily upon .json files. The most current problem is how to handle the picking up and dropping of items in the game. Conceptually, I think I can create a .json file containing player information (among them an inventory), update the inventory with the item's keyword, and delete that keyword from the room's .json file. I would do the opposite if the player were dropping an item.
My game.py:
import cmd
from room import get_room
from item import get_item
import textwrap
class Game(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
#Start the player out in Room #1 by calling the get_room() function
#passing the integer '1' with it, and reading 1.json.
self.loc = get_room(1)
# Displays the room to the player.
self.showRoom()
def move(self, dir):
#Grabs the direction the layer is moving, runs it through the
#_neighbor function within the room module. Determines if there
#is a room in that direction. If so, update to that new room an
#display it.
newroom = self.loc._neighbor(dir)
if newroom is None:
print("You can't go that way.")
else:
self.loc = get_room(newroom)
self.showRoom()
def showRoom(self):
#Displays the current room the player is in, as well as any other
#Objects within it. (Items, enemies, NPCs, etc)
print(self.loc.name)
print("")
#Wraps the text up to 70 words per line.
for line in textwrap.wrap(self.loc.description, 70):
print(line)
print("")
#Looks in the room's .json file to see if the room has a key to
#an item. If it does. Display it on the ground.
if self.loc.haveItem != "None":
print(self.loc.onGround)
print("")
#Looks to the room's .json file to see if the room has a key to
#'showNeighbors.' If it does, display the possible exits of that
#room.
print("Possible Exits:")
print(self.loc.showNeighbors)
def do_look(self, args):
#An exact copy of the showRoom function. There has to be a way to
#point do_look to showRoom() to keep from executing duplicate form.
#This is just bad form.
"""Reprints the rooms description and available exits."""
print(self.loc.name)
print("")
for line in textwrap.wrap(self.loc.description, 70):
print(line)
print("")
if self.loc.haveItem != "None":
print(self.loc.onGround)
print("")
print("Possible Exits:")
print(self.loc.showNeighbors)
def do_get(self, args):
#A function that handles getting an item off the room's ground.
#Currently not functioning as intended. Is meant to find the item
#In the room's .json file, use it to open the items .json file,
#and grab the 'keywords of said item. Then checks to see if
#get <keyword> was typed. If so, edit room's .json file to remove
#item from room, and add it to player inventory. Removing the
#.json file is neccessary to ensure item does not diplay in room
#once picked up.
"""type 'pick <item>' to pick up item in room."""
itemToTake = args.lower()
keywords = self.loc.keywords
if itemToTake == "":
print ("Take what? Type 'look' to see the items to take.")
return
if (itemToTake == keywords[0]) or (itemToTake == keywords[1]):
if self.loc.canTake == "True":
print("You have picked up a " + self.loc.itemName + ".")
#code should be placed here to wipe current room's .json
#file to ensure room is devoid of any items.
else:
print("That item is not here.")
def do_drop(self, args):
#A function that will handle dropping an item from a player's
#inventory. If an item is dropped in inventory, it will be removed
#from players .json inventory file, and added as a key:value
#to the room's .json file, so that should the player return, the
#item will load up as intended.
pass
def do_inv(self):
"""Opens up your inventory"""
#Hasen't been implimented yet. Would like to have it done through a
#.json file. The file would hold other players attributes such as:
#Hit Points, Strength, Dexterity, Armor Class, and the like.
#Self explainatory functions:
def do_quit(self, args):
"""Quit the game"""
print("Thank you for playing.")
return True
def do_n(self, args):
"""Goes North"""
self.move('n')
def do_s(self, args):
"""Goes South"""
self.move('s')
def do_e(self, args):
"""Goes East"""
self.move('e')
def do_w(self, args):
"""Goes West"""
self.move('w')
if __name__ == "__main__":
play = Game()
play.cmdloop()
my room.py:
import json
from item import get_item
"""
This module handles all of the rooms in the game. It searches for a .json file with the approriate id number, then opens it, reads it line by line, and stores it in a dictonary. The Room class then takes the information and sorts it out in to the corrisponding variables. If there is no string listed, it will list the default strings declared in the parameters.
"""
def get_room(id):
ret = None
with open(str(id) + ".json", "r") as file:
jsontext = file.read()
dictonary = json.loads(jsontext)
dictonary['id'] = id
ret = Room(**dictonary)
return ret
class Room():
def __init__(self, id = 0, name="A Room", description = "An Empty Room", neighbors = {}, showNeighbors = "None", haveItem = "None"):
#This is a mess. Has to be a better way to do all this rather than
#place it all in the initiate function. Function assigns .json file
# dictonaries to seperate variables. This is fine.
self.id = id
self.name = name
self.description = description
self.neighbors = neighbors
self.showNeighbors = showNeighbors
self.haveItem = haveItem
#This checks to see if room has an item. If so, grab it's .json file
#and assign it's values to variables. Fell that this SHOULD NOT be
#in __init__. Unsure how to do it any other way.
if haveItem != "None":
item = get_item(haveItem)
self.onGround = item.onGround
onGround = self.onGround
self.keywords = item.keywords
keywords = self.keywords
self.canTake = item.canTake
canTake = self.canTake
self.itemName = item.itemName
itemName = self.itemName
def modifiyRoom(self, id = 0, haveItem = "None"):
pass
#Function used to modify room .json files. uses include:
#Adding a dropped item.
#Removing an item dropped within room.
#Detect if enemy entered or left room.
#At least it would do all of that. If I can get it to work.
def _neighbor(self, direction):
if direction in self.neighbors:
return self.neighbors[direction]
else:
return None
def north(self):
return self._neighbor('n')
def south(self):
return self._neighbor('n')
def east(self):
return self._neighbor('n')
def west(self):
return self._neighbor('n')
and item.py:
import json
"""
This module handles all of the items in the game. It searches for a .json file with the approriate id, then opens it, reads it line by line, and stores it in a dictonary. The Item class then takes the information and sorts it out in to the corrisponding variables. If there is no string listed, it will list the default strings declared in the parameters.
"""
def get_item(itemName):
ret = None
with open(itemName + ".json", "r") as file:
jsontext = file.read()
item = json.loads(jsontext)
item['itemName'] = itemName
ret = Item(**item)
return ret
class Item():
def __init__(self, itemName = "", name = "An Item", onGround = "On ground", description = "A sweet Item", canTake = "False", keywords = "None", value = 0):
#Handles all of the variables found in an item's .json file.
#Feel this will have to act as a super class for more unique
#items, such as weapons, which will hold a damage value.
self.itemName = itemName
self.name = name
self.onGround = onGround
self.description = description
self.canTake = canTake
self.keywords = keywords
self.value = value
A sample room.json:
{"name" : "Disposal Room",
"description" : "A powerful burst of pain in your head wakes you up from your brief slumber, the intense throbbing causing you to see everything in a dull haze. It takes a few moments for the pain to die down, and with it your vision eventually returns to normal. After some examining you discover that you are in some sort of forgotten cell, the prison door to the west of you unlocked and slightly ajar.",
"neighbors" : {"s" : 2},
"showNeighbors" : "south",
"haveItem" : "sword"
}

Related

check if an attribute of a class is assigned to a instance

i'm making a little game for fun and i ran into an issue, how can i see if the email of a member is assigned to a instance?
file1:
if "test1#gmail.com" in file2:
print("ok"
else:
print("not ok")
file2:
player1 = Player("testName1", "test1#gmail.com", "testpwd1"
player2 = Player("testName2", "test2#gmail.com", "testpwd2"
player3 = Player("testName3", "test3#gmail.com", "testpwd3"
player4 = Player("testName4", "test4#gmail.com", "testpwd4"
overriding the __contains__ method will do the trick.
So:
def __contains__(self, key):
return key in self.email
Edit:
If for some reason you don't care which attribute contains the value, this will work:
def __contains__(self, key):
return key in list(self.__dict__.values())
If you put Players in a list then you could use a dictionary to index that list. This example lets you look up by name and email at the same time.
file2.py
class Player:
def __init__(self, name, email, pwd):
self.name = name.lower()
self.email = email.lower()
self.pwd = pwd
players = [
Player("testName1", "test1#gmail.com", "testpwd1"),
Player("testName2", "test2#gmail.com", "testpwd2"),
Player("testName3", "test3#gmail.com", "testpwd3"),
Player("testName4", "test4#gmail.com", "testpwd4")
]
# index to lookup up player by name or email
player_index = {player.name:player for player in players}
player_index.update({player.email:player for player in players})
file1.py
import file2
if file2.player_index.get("test1#mail.com".lower(), None) is not None:
print("ok")
else:
print("not ok")
It would be common to place that players list in some other data structure like a CSV file or even a pickled file. Assuming you want to update players dynamically, it becomes awkward to represent them as class instances in a .py file. But that's an enhancement for another time.

updating class object from main function Python

I have a book library program that it's class reads a file containing ID and name and assign them to self objects as id and card holder. then there is self object for borrowed books, so then I can identify which user borrowed which book.
The problem is in main function when a user return a book, I don't know how to update the self borrowed book object to delete that book from the object, so the self id will not have books borrowed to it.
Here is how the class looks:
#loan time length is 3 weeks by default
LOAN_TIME = 3
class Librarycard:
def __init__(self, card_id, card_holder):
self.__id = card_id
self.__holder = card_holder
#a dictionary that will contain the full book : loan time, updated in later function
self.__loan = {}
def return_book(self, book):
del self.__loan[book]
print('returned')
return
and this is my main function part which concern the book loaning:
def main():
command = input("Command: ")
#borrowed books main list to check if book borrowed or not
borrowed_books = []
if command == "R":
book = input("Book code: ")
if book not in borrowed_books:
print('This book has not been borrowed by anyone')
else:
del borrowed_books[book]
print('book returned')
# this is where I try to enter the function from the class to update the object
# dictionary
book.return_book(book)
if __name__ == "__main__":
main()
A couple of issues here.
First, the book that comes from the input is a string and not the actual book class. So, when calling book.return_book(book), the compiler is looking for a method return_book in the string class (which is obviously not there).
Second, borrowed_books is never being updated in the main class, so the user will never return any books.
Third, instead of using del in the Class, use self.__loan.pop(book). It is the inbuilt python function, and is a better way to remove the book.
Finally, there are a lot of camel case issues
Here is what the final code may look like.
class LibraryCard:
def __int__ (self, card_id, card_holder):
self.card_id=card_id
self.__card_holder=card_holder
self.checked_out={}
def return_book (self, book_id):
popped = self.checked_out.pop(book_id, "Book was already returned, or was never checked out")
if (popped != "Book was already returned, or was never checked out"):
print("Book returned: "+popped)
else:
print(popped)
def isCard(self, id, name):
return self.card_id==id and self.__card_holder==name
def main():
cards = {}
while (true):
command = input("To check out a book press \"C\", to return a book press \"R\" (without the quotes):\t")
print("Whether or not you have an account, please follow the instructions below")
card_id=input("Enter your card id:\t")
user_name=input("For security purposes, enter your name:\t")
if (user_id not in cards):
print("Welcome to Library Services")
cards[user_id] = LibraryCard(card_id, user_name)
if (not cards[user_id].isCard(user_id, user_name)):
print("Wrong user name or user id, please retry")
continue
if (command=="C"):
book=input("Enter the name of the book you would like to check out: ")
cards[user_id].checked_out[book]=3
print("Checked out successfully!")
else:
book=input("Enter the name of the book you would like to return: ")
cards[user_id].return_book(book)
class Librarycard:
def __init__(self, card_id, card_holder):
self.__id = card_id
self.__holder = card_holder
# a dictionary that will contain the full book : loan time, updated in later function
self.__loan = {'tttt': {},"uuuu":{}}
def return_book(self, book):
del self.__loan[book]
print('returned')
return
def main():
# command = input("Command: ")
command = "R"
# borrowed books main list to check if book borrowed or not
borrowed_books = ["tttt","aaaa"]
if command == "R":
# book = input("Book code: ")
book = "tttt"
if book not in borrowed_books:
print('This book has not been borrowed by anyone')
else:
borrowed_books.remove(book)
print('book returned')
library_book = Librarycard('card_id', 'you know holder')
print("before ",library_book._Librarycard__loan)
library_book.return_book(book)
print("after ",library_book._Librarycard__loan)
if __name__ == "__main__":
main()
C:\Users\sharp\AppData\Local\Programs\Python\Python39\python.exe C:/Users/sharp/Desktop/project/testid.py
book returned
before {'tttt': {}, 'uuuu': {}}
returned
after {'uuuu': {}}
Process finished with exit code 0
I have fixed it in a way that I added a new variable that connects the class in the main function, then did a loop that runs into all the cards inside the class keys of the class, and finally with that key I activated the return book function.
The main issue was that I didn't create an access for which ID I wanted to delete the book from. but once activated with the for loop I was able to reach all ID'S object and delete the book from the one that has it.
Here is how the code looks like in the specific parts concerning this problem:
def main():
#function that reads the text file and assign the card ids and card holders as a
#dictionary.
library = read_card_data("library.txt")
while True:
command = input("Command: ")
if command == "R":
book = input("Book code: ")
if book not in borrowed_books:
print('This book has not been borrowed by anyone')
else:
del borrowed_books[book]
for card in library.keys():
library[card].return_book(book)

Video game save/load problems with python pickle (not loading instances)

I'm currently working on a text adventure in python (this language just because it's the one I know), and I'm finding that creating and loading savefiles removes some of the mecahnics I've made. I'll include the problematic code here, rather than all of elements that work fine. it's mainly to do with classes and how instances are 'pickled'.
Here are some of the classes I've created:
class Player:
def __init__(self, name):
self.sack = []
self.kit = []
self.equipped = []
self.log = []
self.done = []
class Weapon:
def __init__(self, name, price, minattack, maxattack):
self.name = name
self.price = price
self.minattack = minattack
self.maxattack = maxattack
class Food:
def __init__(self, name, price, healthadd):
self.name = name
self.price = price
self.healthadd = healthadd
class Quest:
def __init__(self, name, requirement, num, gold, npc, place, give_text, prog_text, comp_text, done_text):
self.name = name
self.requirement = requirement
self.num = num
self.score = 0
self.gold = gold
self.npc = npc
self.place = place
self.give_text = give_text
self.prog_text = prog_text
self.comp_text = comp_text
self.done_text = done_text
The instances in the Player class I've included here are just the ones that are appended by other mechanics with Weapons, Food and Quests. The code includes regions where Weapons, Food and Quests are populated (though working on a separate assets file might tidy things up a bit).
Here's how the save/load functions work currently:
def save(lastplace):
clear()
with open('savefile', 'wb') as f:
PlayerID.currentplace = lastplace.name
pickle.dump(PlayerID, f)
print("Game saved:\n")
print(PlayerID.name)
print("(Level %i)" % (PlayerID.level))
print(lastplace.name)
print('')
option = input(">>> ")
goto(lastplace)
def load():
clear()
if os.path.exists("savefile") == True:
with open('savefile', 'rb') as f:
global PlayerID
PlayerID = pickle.load(f)
savedplace = PlayerID.currentplace
savedplace = locations[savedplace]
goto(savedplace)
else:
print("You have no save file for this game.")
option = input('>>> ')
main()
It's worth noting that upon entry to the game, PlayerID (you) becomes a global variable. You might begin to see some of the issues here, or rather the overarching issue. Essentially, the pickling process serialises all of the possible class types stored in lists within the class of Player just get appended by their names, thus removing their class properties when loaded back into the game.
Is there a pythonic way to ensure that class instances are saved for a future load so that they can still behave as classes, particularly when stacked inside the class of Player? I appreciate this is more of an editorial rather than a question by its length, but any help would be hugely appreciated.

Python dictionary nested within method auto-executes all values (methods) when outer method is called

I'm working on a simple skeleton for a game, and in an effort to try and be more "pythonic", I'm using objects/classes/dictionaries to try and capture all my actions/behaviors (as methods over functions, etc).
For some reason, every time I execute the method 'act' within the class "Player", the dictionary embedded within act runs all of its values (which are, in turn, methods from within the same instance of the class "Player"). In other words, the player chooses "attack, heal, and flee" every time, all at once, before being prompted.
I'm sure there's a simple explanation, but I've been looking for hours and can't find another example of someone's dictionary auto-running all the methods embedded within. Can you help?
Thanks!
- Jake
from random import randint
### BEGIN ALL CLASSES HERE
# To be used for all game objects (living and non-living)
class gameObject(object):
def __init__(self, name):
self.name = name
# To be used for all characters who can act in some way/be killed/change
class livingThing(gameObject):
def __init__(self, name, HP=1):
self.name = name
self.HP = HP
# The playable character(s)
class Player(livingThing):
def __init__(self,name="The Stranger", HP=4, MP=5, strength=1, intellect=1, spirit=1, luck=5, gil=6):
self.name = name
self.HP = HP
self.MP = MP
self.gil = gil
self.strength = strength
self.intellect = intellect
self.spirit = spirit
self.luck = luck
def act(player, enemy):
actions = {
"attack" : player.attack(enemy),
"heal" : player.heal(enemy),
"flee" : player.flee()
}
#Takes input from the player
decision = input("What would you like to do? ")
if decision.lower() in actions:
actions[decision.lower()]
else:
print("That didn't work! Try again.")
# Prints both player and enemy HP
def printHP(player, enemy):
print("{0}'s' HP: {1} \n{2}'s HP: {3}".format(player.name, player.HP, enemy.name, enemy.HP))
# Allows the player to attack an enemy (currently functional)
def attack(player, enemy):
enemy.HP -= player.strength
print("You strike {0} for {1} damage!".format(enemy.name, player.strength))
player.printHP(enemy)
# Allows the player to heal a certain amount of health based on its "spirit" stat (currently functional)
def heal(player, enemy):
healed = randint(0, player.spirit)
player.HP += healed
print("You've healed for {0}!".format(healed))
player.printHP(enemy)
#Allows the player to attempt to run away
def flee(player):
randluck = randint(0, player.luck)
if randluck > 3:
print("You successfully escaped!")
return player.HP
else:
print("You weren't able to escape!")
# Anything that can act with/against the player
class Actor(livingThing):
def __init__(self, name="Unknown Entity", HP=10, MP=2, gil=3):
self. name = name
self.HP = HP
self.MP = MP
self.gil = gil
### END ALL CLASSES ###
### DICTIONARIES CONTAINING ACTIONS ###
### CHARACTERS ###
fighter = Player()
monster = Actor()
fighter.act(monster)
I see the problem. When you are executing Python code, and you have a dictionary as you do, Python evaluates the dictionary fully. If you wanted your values (in your key:value) pairs to be the results of those methods, this is surely one way to do it.
In your case, what you can do is reference the function itself, and not invoke it. You can do this by getting rid of the parentheses, like this:
player.attack
instead of
player.attack()
Then, to call the function you can do something like
actions[decision.lower()](enemy)
Since one of your functions, flee, doesn't accept any parameters, you could give flee a parameter that you simply don't use in the function. If you were designing many many methods that your player can act with, then one strategy would be to give them all only named parameters, like this:
def f1(enemy=None,something=None,foo=None):
if enemy is None:
raise Exception("enemy cannot be None")
#process_enemy
If however, you also have a very high amount of parameters, then you could do this:
def attack(**kwargs):
#kwargs is a dictionary of parameters provided to the function
enemy = kwargs.get('enemy',None)
if enemy is None:
raise Exception("enemy cannot be None")
def eat(**kwargs):
food = kwargs.get('food',None)
if enemy is None:
raise Exception("food cannot be None")
attack(enemy="someenemyobject")
eat(food="somefoodobject")
attack() # raises Exception
attack(food="somefoodobject") # raises Exception
food(enemy="someenemyobject") # raises Exception
food(food="somefoodobject",enemy="someenemyobject") # does not raise Exception

Modifying a function from another in python

For an online course in python, I'm making a basic text-based adventure game in python.
Right now, I have a rudimentary inventory system that works through booleans for if the user has an object or not, and integers for limited items, such as ammo and whatnot.
Here is the code for the inventory system
def Inventory(self): #The inventory for the game. I don't know how to program it properly, so this is my testing ground.
#This will hold the boolean values to if the player has the items or not. Another will be used to show the user the items
street_clothes = False
pistol = False
ammo = 0
phone = False
And this is the code where I am trying to modify the inventory function above
#Eric's apartment
def Eric_Apartment(self):
print "type in grab your gun"
action = raw_input("> ")
if action == "grab":
self.Inventory(CR97) = True
# self.CR97_ammo += 15
# print CR97_ammo
# print self.CR97_ammo
exit(1)
Attempting to run this program gets me this error:
python ex43.py
File "ex43.py", line 78
self.Inventory(CR97) = True
SyntaxError: can't assign to function call
Is there something else I'm supposed to do? I'm very new to python, and this is my first project on my own.
Here is the entire code, for reference
from sys import exit #allows the program to use the exit(1) code
from random import randint #allows the program to use a random number
class Game(object):
#quotes that pop up if the person dies, and also defines the start and self variables
def __init__(self, start):
self.quips = [
"You lose!"
]
self.start = start
def Inventory(self): #The inventory for the game.
#This will hold the boolean values to if the player has the items or not.
street_clothes = False
pistol = False
ammo = 0
phone = False
#this function launches the game, and helps with the room transfer
def play(self):
next = self.start
while True:
print "\n---------"
room = getattr(self, next)
next = room( )
#if the user dies, or fails at the game, this is the function that is ran
def death(self):
print self.quips[randint(0, len(self.quips)-1)]
exit(1)
#Welcome screen to the game
def welcome_screen(self):
print " place holder"
return 'intro_screen'
#Intro screen to the game
def intro_screen(self):
print "place holder"
action = raw_input("> Press any key to continue ")
return 'Eric_Apartment'
#Eric's apartment
def Eric_Apartment(self):
print "type in grab your gun"
action = raw_input("> ")
if action == "grab":
self.Inventory(CR97) = True
# self.CR97_ammo += 15
# print CR97_ammo
# print self.CR97_ammo
exit(1)
a_game = Game("welcome_screen")
a_game.play()
That's an amazingly perverse way to go about it. Why are you using a function to store data?
Just have a player object, with an inventory array.
I'd recommend using objects to model items too. Good use for for a class hierarchy. COuld have a base Item, with Subclasses SingleItem and StackableItem, etc.
Instead of using a function, try using a class -
class Player:
def __init__(self):
self.street_clothes = False
self.pistol = False
self.ammo = 0
self.phone = False
def give_street_clothes(self):
self.street_clothes = True
# etc
But personally, instead of using each item as a boolean, I'd use a list of items:
class Player:
def __init__(self):
self.inventory = []
# add code for ammo/pistol
def has_item(self, item):
return item in self.inventory
def give_item(self, item):
self.inventory.add(item)
def remove_item(self, item):
self.inventory.remove(item)
# etc

Categories

Resources