Im programing a game for school right now. Its going pretty well so far, so Im now trying to implement an undo option after each players turn.
My board is a 7x7 matrix and is defined in __ init __ as self.board.
The board will be manipulated by each players turn.
Besides I have self.saved_states = [] defined in __ init __. This list is supposed to store copies of every state of the game after every players turn.
So far I have a method that actually saves state into the list mentioned in 3.
def save_state(self):
copied_board = copy.copy(self.board)
self.saved_states.append(copied_board)
I tested this and this seems to work. The problem is somewhere else.
So I will get input from user, if he inputs 'u' instead of actual valid game input I want to do the following:
if input == 'u':
print('Undoing last turn!')
self.board = self.saved_states.pop()
self.player_2()
I just want to change the current state of the game to the previous one and have player 2 take their turn (again).
Whats the problem here? Every time each player take its turn Im manipulating elements of the board too. Why can't I just replace the whole board with a previous one? I don't see the difference here.
Thanks for any help or idea!
This is kind of a wild guess: If your self.board is "a 7x7 matrix", it's probably a list of lists, so you should use copy.deepcopy to copy the board. Otherwise, the inner lists will still be the same, and as you modify the current board, you also modify all the "copies".
def save_state(self):
copied_board = copy.deepcopy(self.board)
self.saved_states.append(copied_board)
Alternatively, instead of storing the board layouts for each turn, store the moves performed by the players and replay the game accordingly.
Related
I'm trying to write a text-based adventure in Python, for which I created a class called Room. A Room consists of a description and 4 other Rooms in each direction (north, south, ...).
But when I create, for instance, two rooms next to each other, I have to create one of them first, meaning it can't understand what I pass as the Room next to it because it's a line below.
I was wondering what ways there are to fix such a problem, other than perhaps making and importing a new file for each room. I'll add a small example.
room_north = Room("RoomNorth", room_south)
room_south = Room("RoomSouth", room_north)
Thanks in advance :)
One way is to change your Room class so that it doesn't need the connections at construction. First create the rooms, and then add the connections:
room_north = Room("RoomNorth")
room_south = Room("RoomSouth")
room_north.south = room_south
room_south.north = room_north
Another way is restructure your code to store all rooms in a dictionary, identified by strings:
rooms = {
"room_north": Room("RoomNorth", "room_south"),
"room_south": Room("RoomSouth", "room_north"),
}
A possible drawback is that every lookup of a room will have to go through this dictionary, of course.
To aid in some mental rehabilitation after a nasty hip accident I decided to try and teach myself to program in Python. So I've just started getting my head around defining functions and classes.
I have a basic enemy class that allows me to create an object like this.
enemy01=Enemy("Goblin",10,100,2,5,1,2)
To get the enemy name I can use
foe=enemy01.get_enemyName()
My problem is I want to use a list of enemies which I append as they get killed off and the variable 'foe' to refer to whatever enemy is in play.
So I tried creating a list of the enemy objects, like
currentEnemy=[enemy01, enemy02, enemy03]
and do
foe=currentEnemy.....
But I cant work out how to attach the .get_enemyName()
I was trying things like this to concatenate it
foe=(currentEnemy, ".get_enemyName()")
But nothing I am trying is working when I type 'print(foe)' which is what would be in the main body of code.
I have tried searching online and here but it's really hard as a beginner to put it into words what I am trying to do. Maybe i'm just going about it the wrong way to start with.
I hope I'm making sense and thanks for reading :)
Simon
You seem to conflate the list of all enemies and a variable referring to a particular one.
Here's what you could do:
enemies = [enemy01, enemy02, enemy03]
for currentEnemy in enemies:
eName = currentEnemy.get_enemyName()
print('The current enemy is', eName)
Later on you would probably decide that creating individual enemies is too tedious and use another loop for that:
# create three identical goblins
enemies = [Enemy("Goblin",10,100,2,5,1,2) for _ in range(3)]
If your class :
class Enemy:
def __init__(self,name):
self.name=name
def get_enemyName(self,):
return self.name
and you have initialised currentEnemy as :
enemy01=Enemy('Goblin')
enemy02=Enemy('Rhino')
enemy03=Enemy('DR.Octopus')
currentEnemy=[enemy01, enemy02, enemy03]
And you want to get list of all enemy names into a list foe. Then try :
foe=[x.get_enemyName() for x in currentEnemy]
print foe
i.e. list comprehensions
i= foe.index('Rhino') #find index of Rhino
del[currentEnemy[i]] #let's kill Rhino
print 'current enemies at play {}'.format([x.get_enemyName() for x in currentEnemy])
Sorry - real silly error on my part. I just missed the list index.
I sorted it
enemylist = [enemy01, enemy02, enemy03]
currentEnemy=enemylist[0]
foe=currentEnemy.get_enemyName()
print(foe)
I'm quite green on Python and have been looking around for an answer to my particular question. Though I'm not sure if it's a Python specific question, or if I'm simply getting my OOP / design patterns confused.
I've got three files: main.py, board.py and player.py. Board and player each only hold a class Player and Board, main simply starts the game.
However I'm struggling with validating player positions when they are added to the board. What I want is to instantiate the board and consecutively new player object(s) in main.py, but check the board size in player.py when a new player is added to the board, to ensure the player is not outside of bounds upon creation.
As it is now I'm getting a TypeError (getX() missing 1 required positional argument: 'self') when attempting to access the board's size inside of player.py.
Most likely because the board isn't instantiated in that scope. But if I instantiate it in the scope that will be counted as a new object, won't it? And if I pass the board to the player as a variable that would surely be counted as bad practice, wouldn't it?
So how do I go about accessing the instance variables of one class from another class?
I have no idea if this will help, but I made a post on how to save and load using the Pickle import. In the saving function, it refers back to the Player class I created. It might help you, it might not. Here is the link anyway.
Your question is asking about a concept called "dependency injection." You should take some time to read up on it. It details the ways of making one object available to another object that wants to interact with it. While that's too broad to write up here, here are some of the basics:
You could have all objects you care about be global, or contained in a global container. They can all see each other and interact with each other as necessary. This isn't very object-oriented, and is not the best practice. It's brittle (all the objects are tightly bound together, and it's hard to change or replace one), and it's not a good design for testing. But, it is a valid option, if not a good one.
You could have objects that care about each other be passed to each other. This would be the responsibility of something outside of all of the objects (in your case, basically your main function). You can pass the objects in every method that cares (e.g. board.verify_player_position(player1)). This works well, but you may find yourself passing the same parameter into almost every function. Or you could set the parameter either through a set call (e.g. board.set_player1(player1)), or in the constructor (e.g. board = Board(player1, player2)). Any of these are functionally pretty much the same, it's just a question of what seems to flow best for you. You still have the objects pretty tightly bound. That may be unavoidable. But at least testing is easier. You can make stub classes and pass them in to the appropriate places. (Remember that python works well with duck typing: "If it walks like a duck and quacks like a duck, then it's a duck." You can make testing code that has the same functions as your board and player class, and use that to test your functions.)
A frequent pattern is to have these objects be fairly dumb, and to have a "game_logic" or some other kind of controller. This would be given the instances of the board and the two players, and take care of maintaining all of the rules of the game. Then your main function would basically create the board and players, and simply pass them into your controller instance. If you went in this direction, I'm not sure how much code you would need your players or board to have, so you may not like this style.
There are other patterns that will do this, as well, but these are some of the more basic.
To answer your direct questions: yes, the error you're seeing is because you're trying to invoke the class function, and you need it to be on an object. And yes, instantiating in that case would be bad. But no, passing an instance of one class to another is not a bad thing. There's no use in having objects if they don't interact with something; most objects will need to interact with some other object at some point.
You mentioned that you have code available, but it's a good thing to think out your object interactions a little bit before getting too into the coding. So that's the question for you: do you want player1.check_valid_position(board), or board.check_player(player1), or rules.validate_move(player, some_kind_of_position_variable)`. They're all valid, and they all have the objects inter-relate; it's just a question of which makes the most sense to you to write.
It's hard to know your exact issue without seeing some code, but I hope this is useful!
class Player:
def __init__(self, x, y, player_id):
self.x = x
self.y = y
self.id = player_id
class Board:
def __init__(self, width, height):
self.width = width
self.height = height
self.players = {}
def add_player(self, player):
"""keep track of all the players"""
self._validate_player(player)
# add the player to a dict so we can access them quickly
self.players[player.id] = player
def _validate_player(self, player):
"""whatever validation you need to do here"""
if player.x < 0 or player.x >= self.width:
raise ValueError("The player didn't land on the board! Please check X")
if player.y < 0 or player.y >= self.height:
raise ValueError("The player didn't land on the board! Please check Y")
# whatever other checks you need to do
# player is in a valid location!
def main():
# we can make as few or as many players as we'd like!
p1 = Player(10, 20, 0)
p2 = Player(-1, 10, 1) # invalid player
board = Board(50, 50) # we only need to make one board
board.add_player(p1)
board.add_player(p2) # value error
running = True
while running: # simple game loop
player.take_turn() # maybe prompt user to input something
board.update_players() # update player positions
board.display()
running = board.keep_playing() # check for win state
# whatever code you want to run
if __name__ == "__main__":
main()
Here we create an instance of a Player by assigning an x and y position, and in this case also a player ID which we can use to get that player when we need them. If there's only going to be one player, we could just do something like board.player.
In my example, a ValueError is raised when an invalid Player is provided, you can of course do whatever you'd like in the event that a Player is invalid, also your game could have any number of other cases for a Player being invalid.
I've added some method calls for some methods that might make sense for a board game.
As a side note, in python, you generally don't need to write getters/setters, it's perfectly okay to access Class fields directly.
player.x = 10
if player.y == 11: etc.
and if you have need for validation of some sort that could belong in a getter/setter, you can use the #property decorator.
I am teaching myself Python by reinventing the wheel, i.e. writing a simple console adventure game with interconnected rooms. My interest is not so much in finishing a real game, rather in learning about abstraction and data structures.
I have defined a few basic classes such as World, which contains references to Rooms. These Rooms have Actions and Items. The program checks which Actions are available for each Room and displays them in a list. All of this is working pretty well so I'm trying to complexify things a bit.
I have two problems I can't get my head around. I'll try to explain
them with as little detail as possible.
This is how I defined a possible Action for a Room, in this instance it's a Room referenced by the variable called main (for now data is declared in a module, later to be read from a CSV or XML file):
main.actions = [Action(type = 'move',
result = 'west',
desc = 'Go to the West Room.')]
I need to define an action type because some actions are not movements (e.g. pick up, pull a lever). I'll modify this later to uses subclasses of Action, it's not the issue here.
The 'west' string here refers to the Room which will be the result of the action, i.e. it will become the current room when the action is executed.
However, I'd like the result to be the Room object itself, not a
string ID. But I can't do that until all the Rooms have been initialized.
So I've solved this with the following method of the World object, which works:
def connect_rooms(self):
'''
Once all the rooms have been created,
replace all the string reference to other rooms by the Room objects themselves.
'''
for room in self.rooms:
for action in room.actions:
if action.type == 'move':
room_object = fetch_room(self, action.result)
action.result = room_object
The fetch_room() function (global scope) just does this:
def fetch_room(world, shortname):
# Find a room item by its shortname
for room in world.rooms:
if room.shortname == shortname:
return room
return None
I am sure there is a better way to handle this, since creating connections between nodes seems to be a basic abstract idea.
The other (related) problem is that I'm trying to create conditions built into the Actions themselves, so that the program only proposes them to the player if the conditions defined in the Action are met. For the initial building of the data I can't reference anything else because it hasn't been created yet. I thought of adding a condition in string form and run it later with exec(), but it seems very silly and ugly:
main.actions = [Action(type = 'move',
result = 'west',
desc = 'Go to the West Room.',
conditions = ["player.has('smallkey')"])]
If there is a text somewhere about build such data structures without going insane, I'd be happy to read it.
Thank you.
I think I have all the terms right in my question...but I'm not sure. Basically what I am trying to do is get the little details of my inventory and equipment set in a text game.
What I want to do is have a weapon, lets go with a sword, and i want it to change certain aspects of the character itself. Lets go with the hp of the character increases by like...5, what i would have in this case is, (don't mind the sloppy work!)
global basehp
basehp = 10
global armorhp
armorhp = 0
global skillhp
skillhp = 0
global hpmod
hpmod = (skillhp + armorhp)
global righthand
righthand = 0
so setting the example with hp this is what i actually have in my program, now what i want to do is change armorhp to match what the armor would change it to, say i have my sword that adds 5 to my hp right? what i am working with now is just not working, at first i thought to just have it equip and add 5 to armorhp, then i started thinking about UNequiping it...the effect would be the same...i shall show you what i mean
def hpsword:
armorhp = (armorhp + 5)
def prompt():
x = raw_input("Type command>>")
if x == 'equip':
print "equip what?"
print inventory
y = raw_input(">>>>")
if y == 'sword':
if 'hpsword' in inventory:
global righthand
righthand = 1
inventory.remove ('hpsword')
equipmentlist.append ('hpsword')
Now from right here you'll notice i left some things out, like my lists, i'm getting to that but this is where i can really explain more of what i mean, i want to use each item like that, but i can't figure out how...i'm confusing i know, i have a lot on my mind, but i want to just do something like....
def this sword():
this sword does this thing!
then when it gets equiped add the effect it does, such as +5 health, then remove it and take the effect with it, without having to do 100 lines of code with a bunch of statements that are oh add this effect but then when you don't want to use it anymore reset righthand to 0 and then if it was this number there take this many away from that before you change it to number 2 and add 10 more.
i'm also trying to avoid the equip sword, gain 5 health, unequip, still at 5? good, equip, YAY 10!!!
if i make any sense at all i'm shocked...but if anyone can figure out what i mean and give me a few pointers that would be great!
EDIT:
Reading up on what was left in a comment, (Thanks for that Eevee!) I think I am starting to figure out classes a little...very little.
New question then, would i then make a class for the main character, like,
class character(object):
then add in the init
def __init__(self)
then would i define inventory like,
def __init__(self)
def inventory
or would i do this,
def __init__(self, inventory)
sorry if i ask too many questions, just trying to get it figured out and i've noticed people here are nice enough to at least look at questions from wannabe programmers like myself :D
ANOTHER EDIT!!!!
Again, thanks a ton Eevee! That makes some sense, though reading through it i'm still a little confused as to how i would add it to my code and then what i would need to change for some different items.
To try and explain some, when i say different items, you gave me
class Sword(object):
def visit_health_bonus(self, character):
return 5
and if i wanted a sword that did something different i would just create a new class with a different name, but then basically the same thing?
class Sword2(object):
def visit_strength_bonus(self, character):
return 2
and that would give me a sword that would add 2 to my strength right?
now onto my other question, when i attempt to use this, what would i really need to get it to work right, i'm seeing a lot of things that i don't think would run without having some other bits
class Sword(object):
def visit_health_bonus(self, character):
return 5
# Then, every turn, recalculate health:
health = player.base_health
for equipment in player.equipment: //would i need another class here? (player)
# Pretend this lists everything the player has equipped; you might
# have to check each hand manually
health += equipment.visit_health_bonus(player) //also going to think a class (equipment)
It sounds like you are missing out on some of the awesome utility of object oriented programming, similar to what Marcin said you might what to make a series of classes like so:
class Weapon:
def __init__(self, name, armor_hp_bonus = 0, skill_hp_bonus = 0):
self.skill_hp_bonus = skill_hp_bonus
self.armor_hp_bonus = armor_hp_bonus
self.__name__ = name
class Hero:
def __init__(self):
self.basehp = 10
self.armorhp = 0
self.skillhp = 0
self.righthand = None
self.inventory = []
def choose_weapon(self):
while True:
for idx, item in enumerate(self.inventory):
print '{i}: {weapon}'.format(i=idx, weapon=item.__name__)
choice = raw_input("Pick your weapon: ")
try:
self.equip(self.inventory.pop(int(choice)))
return
except IndexError:
print "Invalid choice"
continue
def equip(self, weapon):
if self.righthand:
print "Weapon already equipped to right hand!"
return
else:
self.righthand = weapon
self.armorhp += weapon.armor_hp_bonus
self.skillhp += weapon.skill_hp_bonus
def unequip(self):
if not self.righthand:
print "No weapon to unequip!"
return
self.skillhp -= self.righthand.skill_hp_bonus
self.armorhp -= self.righthand.armor_hp_bonus
self.inventory.append(self.righthand)
self.righthand = None
def acquire_weapon(self, weapon):
self.inventory.append(weapon)
def calc_effective_hp(self):
return self.basehp + self.skillhp + self.armorhp
A class lets you keep all the variables you are having trouble keeping track of in one place. The skill bonus of one weapong will never get confused with the skill bonus of another weapon this way, because the variable storing that information is contained within the object. One you set up your classes correctly, the rest of your code is a lot shorter and cleaner because you can just call a method and rest assured that everything is being done correctly:
sword_of_1000_truths = Weapon('sword_of_1000_truths', skill_hp_bonus = 1337)
gandalf = Hero()
gandalf.acquire_weapon(sword_of_1000_truths)
gandalf.choose_weapon()
>> 0 sword_of_1000_truths
>> Pick your weapon: 0
print gandalf.calc_effective_hp()
>> 1347
gandalf.unequip()
print gandalf.calc_effective_hp()
>> 10
The methods such as equip and unequip make sure to increment and decrement the Hero's hp correctly, while keeping track of the logic of the game, i.e. you cant have two weapons in one hand, and you cant unequip a weapon if you don't have one equipped etc. This helps eliminate those ugly 100 lines of code you mention.
Good luck! Hope this helps
I'm assuming you're familiar with objects. If not, you should really read up on them (LPTHW is really good) before continuing, as they're pretty central to Python. If so, you should really be using a Player object instead of half a dozen globals.
The really easy approach is to have the sword be an object that does stuff when equipped and unequipped:
class Sword(object):
health_bonus = 5
def on_equip(self, character):
character.health += self.health_bonus
def on_unequip(self, character):
character.health -= self.health_bonus
Then you call on_equip when the player equips the sword, and on_unequip when the player unequips it. Other weapons can have similar event hooks, maybe for different stats.
That's all well and good, but something about it bugs me: you have to remember to call on_unequip whenever the player loses an item, and there might be more than one way that happens. You also have to track the player's "base" health separately, and if it changes, updating the "calculated" health requires unequipping and reequipping the weapon, which doesn't make a lot of sense.
So let's try something else. The "visitor pattern" Marcin referred to means (roughly) to loop through all the parts of a larger whole and call the same method on them. In this case, you want to ask all of the player's equipment how much extra health it grants.
class Sword(object):
def visit_health_bonus(self, character):
return 5
# Then, every turn, recalculate health:
health = player.base_health
for equipment in player.equipment:
# Pretend this lists everything the player has equipped; you might
# have to check each hand manually
health += equipment.visit_health_bonus(player)
Now you calculate the total health every time it's needed, so you don't have to worry about updating it when, say, the player levels up. You also don't have to worry about doing any extra bookkeeping when something gets unequipped: as soon as it disappears from the list of equipment, it stops contributing.
This is still a little inflexible, as it doesn't easily support e.g. a sword that doubles your attack power when fighting dragons. The most general way to solve this involves a lot of tangling and extra objects and figuring out what effects need to happen first, and I've yet to make it work to my satisfaction. Hopefully the above approaches are a good enough starting point. :)
The most flexible way to solve this is to have the action of "attack with equipped weapon" itself be an object and have equipment, the player, the target, etc. all react to the attack by increasing stats. But then you have questions of priority that are really fiddly to resolve so let's not do that.
A couple of things:
You have 5 global variables. These should be moved into a class, and your function become a method on that class.
The usual way to work something like this is for the sword to support a particular interface defining what modifications it makes, which the character class can read. Alternatively, use the visitor pattern, and have the character pass itself to a method of the sword on equip/unequip.