Displaying different data to different users in Python - python

I'm making a (rather) simple card game in Python, I have everything set up for the game, I just need a way to deal with multiple users, and display something (the cards in the hand) to the user that they're assigned to. I've seen some responses about Twisted, but that doesn't seem to solve my problem, at least how it was presented. I'm looking for something like -
print player1cards to player1
print player2cards to player2
but in whatever format is needed.

Well, the obvious answer here would be to have a class Player :
class Player:
playercards = []
Another way is to assign each player a name:
class Player:
name = ""
And then have a Gameserver class :
class Gameserver:
cards = {'Player1':['4Clubs', 'QClubs'], .....}
def getCards(name):
return cards[name]
Then you can do something like this:
gameserver = GameServer()
#Initialize and blablabla
........
x = Player("Player1")
x.showHand()
#the line above would basically do the following:
#print gameserver.getCards(x.name())

Related

Finding class via string

import random
class Game():
def __init__(self, username, gameId):
self.users = []
self.users.append(str(username))
self.gameId = gameId
def new_user(self, username):
self.users.append(str(username))
def remove_user(self, username):
try:
self.users.remove(username)
except:
print("[-] User not found!")
def generate_gameId():
gameId = ""
letters = 5
while(letters>0):
gameId += chr(random.randint(65, 90))
letters-=1
return(gameId)
lobby = []
for i in range(2):
lobby.append(generate_gameId())
lobby[i] = Game("Test", lobby[i])
lobby[i].new_user("Test123")
lobby[i].remove_user("Test123")
This is my code for a simple networking game, I will have multiple Game classes at the same time, but I need to find the specific object of a specific gameId. The gameId is randomly generated. Each time a user wants to join the lobby he has to enter the gameId to enter.
How would you achieve something like this? Am I doing it wrong?
There are some things that can be refactored in your code:
In the constructor of your Game class, there's no need for a username parameter, since there's already a new_user method:
class Game():
def __init__(self, gameId):
# Just create the list of users
self.users = []
self.gameId = gameId
# ...
lobby = []
for i in range(2):
lobby.append(generate_gameId())
lobby[i] = Game(lobby[i])
# Use the `new_user` method to create a Test
lobby[i].new_user("Test")
lobby[i].new_user("Test123")
lobby[i].remove_user("Test123")
You're storing the ids in an integer list. You should use a dictionary given that a game will have an unique id:
lobby = {}
for i in range(2):
game_id = generate_gameId()
game = Game(game_id)
# Create a entry in the dictionary
lobby[game_id] = game
game.new_user("Test")
game.new_user("Test123")
game.remove_user("Test123")
Then, you can access the list of games and their ids:
for game_id, game in lobby.items():
print(f'The game {game_id} has the following users:')
for user in game.users:
print(user)
print()
The other guys said everything I was going to say so I deleted most of my post, but here's some other things you could improve on if you want:
You are not looking up a "Class" here. You're looking up an instance of a class, otherwise known as an object. The word "class" in programming always means "The definition of an object". Classes can be instantiated to make objects AKA instances. A good analogy is that a "class" is the blueprints for making a car, while the instance/object would be the car itself that was made using the blueprints(the class).
Don't combine naming conventions. You're combining camel case and snake case which is never a good idea, choose one or the other (python is usually snake case). Specifically, generate_gameId() should be generate_game_id(). This just makes it easier to write code without making spelling mistakes.

Inheriting class attributes without declaring attributes or better OOP

I am attempting to construct classes to play out a game of MTG (A card game). I have three relevant classes: MTGGame(...), MTGCard(...), and AbilityList(). An object of MTGGame has several attributes about the player (turn, mana,..., deck).
A player must have a deck of cards to play, so I create a list of MTGCard objects for each player that is a deck, and create an MTGGame object for each from the respective decks. The cards have abilities, and when creating the cards I store abilities as functions/params into each MTGCard. However, I need the abilities to inherit and access methods/attributes from MTGGame and update them, but if I use super().__init__, then I will need to call my deck as a parameter for AbilityList when making MTGCards, which I wouldn't have yet.
Can this be achieved? If not, any suggestions improving my OOP logic to achieve this task?
I am aware that I can do something like this:
class MTGGame():
def __init__(self, deck, turn = 0, mana = 0, lifeTotal = 20, cavalcadeCount = 0, hand = [], board = []):
self.turn = turn
self.mana = mana
self.lifeTotal = lifeTotal
...
def gainLife(self, lifeGained):
self.lifeTotal = self.lifeTotal +lifeGained
def combatPhase(self):
for card in self.board:
card.attackingAbility()
class MTGCard():
def __init__(self, name, CMC, cardType, power, toughness, castedAbility, attackingAbility, activatedAbility, canAttack = False):
....
self.attackingAbility = attackingAbility
Class abilityList():
def healersHawkAbility(self, lifeAmt):
MTGGame.gainLife(lifeAmt)
But this would affect all instances of MTGGame, not the specific MTGGame object this would've been called from. I'd like it to simply update the specific object in question. I'd like to do something like this but I don't know how abilityList methods could access MTGGame attributes/methods ('AbilityList' object has no attribute 'gainLife'):
Class abilityList():
def healersHawkAbility(self, lifeAmt):
#How do I access methods/attributes in MTGGame from here? self?
self.gainLife(lifeAmt)
aL = abilityList()
#One example card:
card1 = MTGCard("Healers Hawk",1,'Creature',1,1, aL.nullAbility(), aL.healersHawkAbility, aL.nullAbility())
whiteDeck = [list of constructed MTGCard() objects, card1, card2,...,cardLast]
player1 = MTGGame(whiteDeck)
...
#Call the ability in a method contained in MTGGame:
player1.combatPhase()
#Would call something like this inside
card.attackingAbility()
#Which may refer to card.healersHawkAbility() since we stored healersHawkAbility() as an attribute for that MTGCard,
#and would declare gainLife(), which refers to self.lifeTotal or player1.lifeTotal in this case.
This is an excellent start and clearly you have already thought a lot of this through. However, you haven't thought through the relationship between the classes.
First thing to note:
MTGGame.gainLife(lifeAmt) is a method call accessed via the class rather than an instance. This means that the self paramter is not actually filled in i.e. you will get an error becuase your method expects 2 arguments but only receive one.
What you perhaps meant to do is the following:
class MTGGame:
lifeTotal = 20 # Notice this is declared as a class variable
def __init__(self, ...):
...
#classmethod
def healersHawkAbility(cls, lifeGained):
cls.lifeTotal = cls.lifeTotal + lifeGained
However, this requires class variables which here defeats the point of having an instance.
Your naming throughout the program should suggest that your classes are a little off.
For instance player1 = MTGGame(). Is player a game? No, of course not. So actually you might want to rename your class MTGGame to Player to make it clear it refers to the player, not the game. A seperate class called MTGGame will probably need to be created to manage the interactions between the players e.g. whose turn it is, the stack holding the cards whilst resolving.
The main focus of your question: how to deal with the cards accessing the game/player object.
Cards should be able to access instances of the player and game classes, and if the player has a is_playing attribute, the card should not have this. The rule of thumb for inheritance is 'is a'. Since card 'is not a' player, it should not inherit from it or MTGGame. Instead, card should be like this for example:
game = RevisedMTGGame()
player1 = Player()
player2 = Player()
class Card:
def __init__(self, name, text, cost):
self.name = name
self.text = text
self.cost = cost
self.owner = None
self.game = None
class Creature(Card):
def __init__(self, name, text, cost, power, toughness):
super().__init__(self, name, text, cost)
self.power = power
self.toughness = toughness
def lifelink(self):
self.owner.heal(self.power) # NOTE: this is NOT how I would implement lifelink, it is an example of how to access the owner
healersHawk = Creature("Healer's Hawk", "Flying, Lifelink", 1, 1, 1)
healersHawk.game = game
healersHawk.owner = player1
You can see from this incomplete example how you can set up your cards easily, even with complex mechanics, and as the base classes have been defined you can avoid repitition of code. You might want to look into the event model in order to implement the lifelink mechanic, as an example. I wish you luck in continuing your game!

Accessing Classes via Variable

I am attempting to make a small game, and what I will be doing is using classes to dictate the stats of the user's preferred class. I need to make the classes so when it comes time to fight a monster (which will also be in it's own class), I will be able to have their stats callable.
This may be a "big picture" problem, but to avoid writing the code three times (for each class), I only want to call the variable "chosenClass.HP" instead of "Mage.HP" because if I did it that way, I would need to have a bunch of if statements for each class among the story. There must be a better way.
I have worked on this both ways, and I hate having to write
if userChoice is 'Mage':
HP = 150
I have looked around at the self.something, but I don't completely understand how that works. If that is the required or recommended solution, what is the best resource for learning?
print("Welcome to the game")
name = input("Choose your name\n")
print(name)
class Warrior:
HP = 100
ATK = 200
DEF = 0
class Mage:
HP = 100
ATK = 200
DEF = 0
class Rouge:
HP = 150
ATK = 250
DEF = 100
user_class = input("Choose a class between Warrior, Mage, or Rouge\n")
print(user_class)
while(user_class != 'Warrior' and user_class != 'Mage' and user_class != 'Rouge'):
print("You did not choose from the required classes, try again")
user_class = input()
theClass = user_class()
theClass.HP = 1000
My error that I get from this code is:
TypeError: 'str' object is not callable
I understand it's being given a String, but it's a variable. Why is it causing problems?
Any recommendations to make this program better?
user_class returns a string and not an actual Class that you've defined above. This is because input() returns a string and you store that in user_class. In order to create a new instance of the classes you created in your code need to instantiate the class using some sort of logic based on the player's input such as:
if user_class.lower() == 'warrior':
theClass = Warrior()
theClass.HP = 1000
Obviously this wouldn't be the best way but is just an example to show how to instantiate the class.
You need a mapping of strings to classes if you want to create an instance like this.
pc_classes = {'Mage': Mage, 'Warrior': Warrior, 'Rogue': Rogue}
while True:
try:
user_class = input("Enter Mage, Warrior, or Rogue)
if user_class in pc_classes:
break
pc = pc_classes[user_class]()

Better way to create my lists in classes?

I've been working on making my text based game more realistic, and one design I would like to implement is to make sure the rooms stay 'static' to a point (i.e. a player uses a potion in the room, if they come back to that room that potion should no longer be there.)
This is how my code is basically set up (I have been using "Learn Python The Hard Way" by Zed Shaw so my code is set up much in the same way):
class Scene(object):
def enter(self):
print "This scene is not yet configured. Subclass it and implement enter()."
class Room(Scene):
potions = []
for i in range(3):
potions.append(Potion())
def enter(self):
...
When I run this, I get a NameError: global name 'potions' is not defined. I know I can fix this one of two ways: 1. make potions a global variable, but then I would have to make a new list for each room that contains potions (There are 36 rooms in total, set up as a 6x6 grid) OR
2. Put this line of code in the enter function, but that would cause the list to reset to 3 potions each time the user enters the room.
potions = []
for i in range(3):
potions.append(Potion())
If there's no other way, I suppose declaring a new variable for all the rooms that contain potions (There's only 5). But my question is if there's another way of making this work without making it a global.
Thanks for your help!
First, let's look at your example (I'll simplify it):
class Room(Scene):
potions = [Potion() for x in range(3)]
What you have done there is create a class attribute potions that are shared among all instances of Room. For example, you'll see my potions in each of my rooms are the same instances of potions (the hex number is the same!). If I modify the potions list in one instance, it modifies the same list in all of the Room instances:
>>> room1.potions
[<__main__.Potion instance at 0x7f63552cfb00>, <__main__.Potion instance at 0x7f63552cfb48>, <__main__.Potion instance at 0x7f63552cfb90>]
>>> room2.potions
[<__main__.Potion instance at 0x7f63552cfb00>, <__main__.Potion instance at 0x7f63552cfb48>, <__main__.Potion instance at 0x7f63552cfb90>]
>>>
It sounds like you want potions to be a unique attribute of each instance of a Room.
Somewhere you will be instantiating a room, e.g., room = Room(). You need to write your constructor for your Room in order to customize your instance:
class Room(Scene):
def __init__(self): # your constructor, self refers to the Room instance.
self.potions = [Potion() for x in range(3)]
Now when you create your room instance, it will contain 3 potions.
You now need to think about how you will make your rooms instances persist between entrances by your characters. That will need to be some sort of variable that persists throughout the game.
This idea of object composition will extend through your game. Perhaps you have a Dungeon class that has your 36 rooms:
class Dungeon(object):
def __init__(self):
self.rooms = [[Room() for x in range(6)] for x in range(6)]
Or perhaps your rooms have up to four doors, and you link them up into something potentially less square:
class Room(Scene):
def __init__(self, north_room, east_room, south_room, west_room):
self.north_door = north_room
self.east_door = east_room
[... and so on ...]
# Note: You could pass `None` for doors that don't exist.
Or even more creatively,
class Room(Scene):
def __init__(self, connecting_rooms): # connecting_rooms is a dict
self.connecting_rooms = connecting_rooms
Except both examples will get you a chicken and egg problem for connecting rooms, so it is better to add a method to add each room connection:
class Room(Scene):
def __init__(self):
self.rooms = {}
# ... initialize your potions ...
def connect_room(self, description, room):
self.rooms[description] = room
Then you could do:
room = Room()
room.connect_room("rusty metal door", room1)
room.connect_room("wooden red door", room2)
room.connect_room("large hole in the wall", room3)
Then perhaps your dungeon looks like this:
class Dungeon(Scene):
def __init__(self, initial_room):
self.entrance = initial_room
Now in the end, you just have to hold onto your dungeon instance of Dungeon for the duration of the game.
btw, this construct of "rooms" connected by "paths" is called a Graph.

Creating dynamic variables for a class from within the class

For context, I'm working on an inventory system in an RPG, and I'm prototyping it with python code.
What I don't understand is how to make separate variables for each instance of an item without declaring them manually. For a short example:
class Player(object):
def __init__(self):
self.Items = {}
class Item(object):
def __init__(self):
self.Equipped = 0
class Leather_Pants(Item):
def __init__(self):
#What do i place here?
def Pick_Up(self, owner):
owner.Items[self.???] = self #What do i then put at "???"
def Equip(self):
self.Equipped = 1
PC = Player()
#Below this line is what i want to be able to do
Leather_Pants(NPC) #<-Create a unique instance in an NPC's inventory
Leather_Pants(Treasure_Chest5) #Spawn a unique instance of pants in a treasure chest
Leather_Pants1.Pick_Up(PC) #Place a specific instance of pants into player's inventory
PC.Items[Leather_Pants1].Equip() #Make the PC equip his new leather pants.
If I did something silly in the above code, please point it out.
What I want to do if the code doesn't make it clear is that I want to be able to dynamically create variables for all items as I spawn them, so no two items will share the same variable name which will serve as an identifier for me.
I don't mind if I have to use another class or function for it like "Create_Item(Leather_Pants(), Treasure_Chest3)"
What's the best way to go about this, or if you think I'm doing it all wrong, which way would be more right?
As a general rule, you don't want to create dynamic variables, and you want to keep data out of your variable names.
Instead of trying to create variables named pants0, pants1, etc., why not just create, say, a single list of all leather pants? Then you just do pants[0], pants[1], etc. And none of the other parts of your code have to know anything about how the pants are being stored. So all of your problems vanish.
And meanwhile, you probably don't want creating a Leather_Pants to automatically add itself to the global environment. Just assign it normally.
So:
pants = []
pants.append(Leather_Pants(NPC))
pants.append(Leather_Pants(chests[5]))
pants[1].pickup(PC)
The pants don't have to know that they're #1. Whenever you call a method on them, they've got a self argument that they can use. And the player's items don't need to map some arbitrary name to each item; just store the items directly in a list or set. Like this:
class Player(object):
def __init__(self):
self.Items = set()
class Item(object):
def __init__(self):
self.Equipped = 0
class Leather_Pants(Item):
def __init__(self):
pass # there is nothing to do here
def Pick_Up(self, owner):
self.owner.Items.add(self)
def Equip(self):
self.Equipped = 1
Abernat has tackled a few issues, but I thought I weigh in with a few more.
You appear to be using OOP, but are mixing a few issues with your objects. For example, my pants don't care if they are worn or not, I care though for a whole host of reasons. In python terms the Pants class shouldn't track whether it is equipped (only that it is equippable), the Player class should:
class CarryableItem:
isEquipable = False
class Pants(CarryableItem):
isEquipable = True
class Player:
def __init__(self):
self.pants = None # Its chilly in here
self.shirt = None # Better take a jumper
self.inventory = [] # Find some loot
def equip(self,item):
if is.isEquipable:
pass # Find the right slot and equip it,
# put the previously equipped item in inventory, etc...
Also, its very rare that an item will need to know who its owner is, or that its been grabbed, so verbs like that again should go onto the Player:
class Player:
maxCarry = 10
def take(Item):
if len(inventory) < maxCarry:
inventory.append(item)
Lastly, although we've tried to move most verbs on to actors which actually do things, sometimes this isn't always the case. For example, when instantiating a chest:
import random
class StorageItem:
pass
class Chest(StorageItem):
__init__(self):
self.storage = random.randint(5)
self.loot = self.spawnLoot()
def spawnLoot(self):
for i in range(self.storge):
# Lets make some loot
item = MakeAnItem # Scaled according to type level of dungeon, etc.
loot.append(item)
def remove(item):
self.loot[self.loot.index(item)]=None
Now the question about what to do when a Player wants to plunder a chest?
class Player:
def plunder(storage):
for item in storage.loot:
# do some Ui to ask if player wants it.
if item is not None and self.wantsItem(item) or \
(self.name="Jane" and self.wantsItem(item) and self.doesntWantToPay):
self.take(item)
storage.remove(item)
edit: Answering the comment:
If you are curious about calculating armor class, or the like, that again is a factor of the user, not the item. For example:
class Player:
#property
def clothing(self):
return [self.pants,self.top]
#property
def armorClass(self):
ac = self.defence
for item in self.clothing:
def = item.armorClass
if self.race="orc":
if item.material in ["leather","dragonhide"]:
def *= 1.5 # Orcs get a bonus for wearing gruesome dead things
elif item.material in ["wool","silk"]:
def *= 0.5 # Orcs hate the fineries of humans
ac += def
return ac
pants = Pants(material="leather")
grum = Player(race="orc")
grum.equip(pants)
print grum.armorClass
>>> 17 # For example?

Categories