Related
I'm a noob, working on making the Yahtzee game I programmed multiplayer. In Python 2.7, I want to have a prompt where a user can enter the number of players, i.e. 2, 3,4, 2007 etc., after which for the number of players entered, i.e. 3, the user will enter the names of the players, i.e. Mike, Tom, Jim, which I can then use in my program to keep score (i.e. Mike's score is 7, he's pretty bad, Jim has 250, he's pretty good etc.). I've seen suggestions to use dictionaries, classes and arrays, but I'm lost as to which one is best, and worst still, can't make what I'm trying to do work.
from collections import defaultdict
d = defaultdict(int)
d = {}
players = raw_input('How many players?')
players = int(players)
for i in range (1,players+1):
d = raw_input('Enter player name')
print d
my code on Repl.it is here
In your for loop you are assigning whatever the player types in as a name to be equal to d. d is therefore instantly no longer referring to any dictionary, but a string.. whatever the name was. A particular variable can only refer to one object at a time. Logically this is straight-forward if you think about what happens when you re-assign a variable to something new, how could the interpreter possibly differentiate between multiple possible objects when all it's provided is the label d..
Try something like this, a dict seems good to me:
players_dict = {}
num_players = raw_input("Enter number of players: ")
for i in num_players:
name = raw_input("Enter name for player %s:" % i)
# set initial score to 0
players_dict[name] = 0
Thanks all for the help. With your help, I figured out the following code (using a list and appending to it), which worked:
player_names = []
input_players = int(raw_input('how many players?'))
for i in range(0,input_players):
name = raw_input('enter player name')
name = name.upper()
player_names.append(name)
#using my new player names to iterate through turns:
for i in range(0,13):
for i in player_names:
print i # placeholder for my turn function
I have the following code in python 2.7:
net = {'Freda': [['Olive', 'John', 'Debra'], ['Starfleet Commander', ' Ninja Hamsters', ' Seahorse Adventures']], 'Ollie': [['Mercedes', 'Freda', 'Bryant'], ['Call of Arms', ' Dwarves and Swords', ' The Movie: The Game']], 'Debra': [['Walter', 'Levi', 'Jennie', 'Robin'], ['Seven Schemers', ' Pirates in Java Island', ' Dwarves and Swords']]}
def get_secondary_connections(network, person):
if person in network:
for person in network:
connections = network[person][0]
result = connections
for connection in connections:
result = result + get_secondary_connections(network, connection)
return result
return None
print get_secondary_connections(net, 'Fred')
When I execute it gives the following error:
result = result + get_secondary_connections(network, connection)
RuntimeError: maximum recursion depth exceeded
Please tell me where I went wrong.
First, pay attention to the semantics: use lists [x, y, z] for a collection which you're intending to loop through; use tuples (x, y, z) for a fixed-length collection which you're intending to index into, which is not big enough to become its own class. So you should have
net = {
'Freda': (['Olive', 'John', 'Debra'],
['Starfleet Commander', ' Ninja Hamsters', ' Seahorse Adventures']),
'Ollie': (['Mercedes', 'Freda', 'Bryant'],
['Call of Arms', ' Dwarves and Swords', ' The Movie: The Game']),
'Debra': (['Walter', 'Levi', 'Jennie', 'Robin'],
['Seven Schemers', ' Pirates in Java Island', ' Dwarves and Swords'])
}
Second, when doing a recursion problem, step through what you want to happen with some examples first, as if you were the machine. Your first task when processing Freda is to load her connections Olive, John, Debra. What do you want to do with each of these? Well you're going to try to load Olive's connections, fail, try to load John's connections, fail, then try to load Debra's connections, and then you'll have Walter, Levi, Jennie, Robin. What do you want to do with this list? Return it? Concatenate with anyone else's friends? There's nothing "recursive" about the secondary connections, at least not as one would normally think. It would in other words seem to match the name of the function to define:
def get_secondary_connections(network, person):
primary_connections, movies = network.get(person, ([], []))
out = []
for friend in primary_connections:
secondary_connections, movies = network.get(person, ([], []))
out.extend(secondary_connections)
return out
No recursion needed. When do we need recursion? Well, if we wanted to find everyone who this person is connected to by a friend or a friend-of-a-friend or a friend-of-a-friend-of-a-friend, then we need to explore the whole graph and recursion might be helpful. Of course we might also want this thing to not contain duplicates, so we might want to use a set rather than a list.
A first stab might be:
def get_all_connections(network, person):
primary_connections, movies = network.get(person, ([], []))
out = set(primary_connections) # copy this list into an output set
for friend in primary_connections:
out = out.union(get_all_connections(network, person))
return out
And now you will discover that indeed, in the wrong sort of network, this thing will easily exceed a maximum recursion depth. A simple network that does this:
net = {
'Alice': (['Bob'], []),
'Bob': (['Alice'], [])
}
Why does this happen? Because to find all of Alice's friends you need to find all of Bob's friends, but to find all of Bob's friends you need to find all of Alice's friends. So how do we get past this?
We can ask for all of Bob's friends excluding those that come through Alice. This will need to be a full list otherwise we will just trip up on other cyclic cases, like:
net = {
'Alice': (['Bob'], []),
'Bob': (['Carol', 'Dylan'], []),
'Carol': (['Alice'], []),
'Dylan': (['Bob'], [])
}
note that when we ask for Bob's friends we will recurse on both Carol and Dylan, we need to tell both of them to exclude both Alice and Bob, as already being handled. So that leads to
def get_all_connections(network, person, excluding=()):
if person in excluding:
return set() # we exclude by immediately returning the empty set.
excluding_us_too = excluding + (person,)
primary_connections, movies = network.get(person, ([], []))
out = set(primary_connections)
for friend in primary_connections:
out = out.union(get_all_connections(network, person, excluding_us_too))
return out
This cycle-detection strategy is known by a few different names, but usually the tuple here is called a "path" since when we process any of Dylan's friends it says ('Dylan', 'Bob', 'Alice'), meaning that we got to Dylan's friends by visiting first Alice and then Bob and then Dylan.
Notice that 'Carol' is nowhere in Dylan's path; this can sometimes be a good thing and sometimes not: if Dylan connects to Carol and get_all_connections applied to Carol produces a million results even when excluding Alice and Bob and Dylan, then we can expect both of these to produce the same million results for Dylan, and then when we get to Bob we have to union these two million results into a set of only one million -- that's a lot of unnecessary work!
So another stab would then be to keep a queue of people-to-handle and people-we've-handled. At this point you would not want so much to use recursion, you'd rather use normal looping constructs.
def get_all_connections(network, person):
visited, to_visit, out = set(), set([person]), set()
while len(to_visit) > 0:
visiting = to_visit.pop()
visited.add(visiting)
connections, movies = network.get(visiting, ([], []))
for friend in connections:
out.add(friend)
if friend not in visited:
to_visit.add(friend)
return out
A loop like this can be converted into an efficient recursion, but unfortunately Python does not make this style of recursion efficient, making the exercise academic rather than practical.
I'm making a text-based adventure game, and would like to have a universal 'look' function that uses an algorithm to tell the player how many and what objects are in a room instead of me having to write individual descriptions for each room. So it would work roughly like this:
lookround(things.bedroom)
You see:
a bed, which is the last on the right, across from Jacob's and to the left of Steve's,
and
a calendar, which is on a nail driven into the wall to the left of your bed
The objects are stored in the class 'things', which has a format that organises them with the object name first, and then the description of its location, then it repeats. That way, all the function has to do is print the first two tuples, then the next two, then the next two, and so on.
So, how would I get it to print out a number of tuples which have not been spoon fed to it?
Right now, I'm trying to use this:
def lookround(room):
print '''You see:'''
for len(room) % 2:
print ('{}, which is {},'.format(room))
The problems I'm having are that I'm getting a syntax error which points to the colon after len, and I'm not sure what I should put in .format() .
I've tried messing around with the syntax, but nothing's working.
class room(object):
things = ('a bed', 'to sleep in',
'a calendar', 'to look up the date',
'a clock', 'to wake up')
def lookround(room):
print '''You see:'''
for i in range(len(room.things)):
if not (i%2):
print ('{}, which is {},'.format(room.things[i], room.things[i+1]))
if i != len(room.things) - 2:
print 'and'
This should work with your current format. It might be a better idea to store things as a tuple of tuples, so you don't deal with the modulus business...
Or maybe I should say, ways to skip having to initialize at all.
I really hate that every time I want to do a simple count variable, I have to say, "hey python, this variable starts at 0." I want to be able to say count+=1and have it instantly know to start from 0 at the first iteration of the loop. Maybe there's some sort of function I can design to accomodate this? count(1) that adds 1 to a self-created internal count variable that sticks around between iterations of the loop.
I have the same dislike for editing strings/lists into a new string/list.
(Initializing new_string=""/new_list=[] before the loop).
I think list comprehensions may work for some lists.
Does anyone have some pointers for how to solve this problem? I am fairly new, I've only been programming off and on for half a year.
Disclaimer: I do not think that this will make initialization any cleaner. Also, in case you have a typo in some uses of your counter variable, you will not get a NameError but instead it will just silently create and increment a second counter. Remember the Zen of Python:
Explicit is better than implicit.
Having said that, you could create a special class that will automatically add missing attributes and use this class to create and auto-initialize all sorts of counters:
class Counter:
def __init__(self, default_func=int):
self.default = default_func
def __getattr__(self, name):
if name not in self.__dict__:
self.__dict__[name] = self.default()
return self.__dict__[name]
Now you can create a single instance of that class to create an arbitrary number of counters of the same type. Example usage:
>>> c = Counter()
>>> c.foo
0
>>> c.bar += 1
>>> c.bar += 2
>>> c.bar
3
>>> l = Counter(list)
>>> l.blub += [1,2,3]
>>> l.blub
[1, 2, 3]
In fact, this is similar to what collections.defaultdict does, except that you can use dot-notation for accessing the counters, i.e. c.foo instead of c['foo']. Come to think of it, you could even extend defaultdict, making the whole thing much simpler:
class Counter(collections.defaultdict):
def __getattr__(self, name):
return self[name]
If you are using a counter in a for loop you can use enumerate:
for counter, list_index in enumerate(list):
the counter is the first variable in the statement and 1 is added to it per iteration of the loop, the next variable is the value of that iteration in the list. I hope this answers your first question as for your second, the following code might help
list_a = ["this", "is"]
list_b = ["a", "test"]
list_a += list_b
print(list_a)
["this", "is", "a", "test"]
The += works for strings as well because they are essentially lists aw well. Hope this helps!
I'm working with the program Autodesk Maya.
I've made a naming convention script that will name each item in a certain convention accordingly. However I have it list every time in the scene, then check if the chosen name matches any current name in the scene, and then I have it rename it and recheck once more through the scene if there is a duplicate.
However, when i run the code, it can take as long as 30 seconds to a minute or more to run through it all. At first I had no idea what was making my code run slow, as it worked fine in a relatively low scene amount. But then when i put print statements in the check scene code, i saw that it was taking a long time to check through all the items in the scene, and check for duplicates.
The ls() command provides a unicode list of all the items in the scene. These items can be relatively large, up to a thousand or more if the scene has even a moderate amount of items, a normal scene would be several times larger than the testing scene i have at the moment (which has about 794 items in this list).
Is this supposed to take this long? Is the method i'm using to compare things inefficient? I'm not sure what to do here, the code is taking an excessive amount of time, i'm also wondering if it could be anything else in the code, but this seems like it might be it.
Here is some code below.
class Name(object):
"""A naming convention class that runs passed arguments through user
dictionary, and returns formatted string of users input naming convention.
"""
def __init__(self, user_conv):
self.user_conv = user_conv
# an example of a user convention is '${prefix}_${name}_${side}_${objtype}'
#staticmethod
def abbrev_lib(word):
# a dictionary of abbreviated words is here, takes in a string
# and returns an abbreviated string, if not found return given string
#staticmethod
def check_scene(name):
"""Checks entire scene for same name. If duplicate exists,
Keyword Arguments:
name -- (string) name of object to be checked
"""
scene = ls()
match = [x for x in scene if isinstance(x, collections.Iterable)
and (name in x)]
if not match:
return name
else:
return ''
def convert(self, prefix, name, side, objtype):
"""Converts given information about object into user specified convention.
Keyword Arguments:
prefix -- what is prefixed before the name
name -- name of the object or node
side -- what side the object is on, example 'left' or 'right'
obj_type -- the type of the object, example 'joint' or 'multiplyDivide'
"""
prefix = self.abbrev_lib(prefix)
name = self.abbrev_lib(name)
side = ''.join([self.abbrev_lib(x) for x in side])
objtype = self.abbrev_lib(objtype)
i = 02
checked = ''
subs = {'prefix': prefix, 'name': name, 'side':
side, 'objtype': objtype}
while self.checked == '':
newname = Template (self.user_conv.lower())
newname = newname.safe_substitute(**subs)
newname = newname.strip('_')
newname = newname.replace('__', '_')
checked = self.check_scene(newname)
if checked == '' and i < 100:
subs['objtype'] = '%s%s' %(objtype, i)
i+=1
else:
break
return checked
are you running this many times? You are potentially trolling a list of several hundred or a few thousand items for each iteration inside while self.checked =='', which would be a likely culprit. FWIW prints are also very slow in Maya, especially if you're printing a long list - so doing that many times will definitely be slow no matter what.
I'd try a couple of things to speed this up:
limit your searches to one type at a time - why troll through hundreds of random nodes if you only care about MultiplyDivide right now?
Use a set or a dictionary to search, rather than a list - sets and dictionaries use hashsets and are faster for lookups
If you're worried about maintining a naming convetion, definitely design it to be resistant to Maya's default behavior which is to append numeric suffixes to keep names unique. Any naming convention which doesn't support this will be a pain in the butt for all time, because you can't prevent Maya from doing this in the ordinary course of business. On the other hand if you use that for differntiating instances you don't need to do any uniquification at all - just use rename() on the object and capture the result. The weakness there is that Maya won't rename for global uniqueness, only local - so if you want to make unique node name for things that are not siblings you have to do it yourself.
Here's some cheapie code for finding unique node names:
def get_unique_scene_names (*nodeTypes):
if not nodeTypes:
nodeTypes = ('transform',)
results = {}
for longname in cmds.ls(type = nodeTypes, l=True):
shortname = longname.rpartition("|")[-1]
if not shortname in results:
results[shortname] = set()
results[shortname].add(longname)
return results
def add_unique_name(item, node_dict):
shortname = item.rpartition("|")[-1]
if shortname in node_dict:
node_dict[shortname].add(item)
else:
node_dict[shortname] = set([item])
def remove_unique_name(item, node_dict):
shortname = item.rpartition("|")[-1]
existing = node_dict.get(shortname, [])
if item in existing:
existing.remove(item)
def apply_convention(node, new_name, node_dict):
if not new_name in node_dict:
renamed_item = cmds.ls(cmds.rename(node, new_name), l=True)[0]
remove_unique_name(node, node_dict)
add_unique_name ( renamed_item, node_dict)
return renamed_item
else:
for n in range(99999):
possible_name = new_name + str(n + 1)
if not possible_name in node_dict:
renamed_item = cmds.ls(cmds.rename(node, possible_name), l=True)[0]
add_unique_name(renamed_item, node_dict)
return renamed_item
raise RuntimeError, "Too many duplicate names"
To use it on a particular node type, you just supply the right would-be name when calling apply_convention(). This would rename all the joints in the scene (naively!) to 'jnt_X' while keeping the suffixes unique. You'd do something smarter than that, like your original code did - this just makes sure that leaves are unique:
joint_names= get_unique_scene_names('joint')
existing = cmds.ls( type='joint', l = True)
existing .sort()
existing .reverse()
# do this to make sure it works from leaves backwards!
for item in existing :
apply_convention(item, 'jnt_', joint_names)
# check the uniqueness constraint by looking for how many items share a short name in the dict:
for d in joint_names:
print d, len (joint_names[d])
But, like i said, plan for those damn numeric suffixes, maya makes them all the time without asking for permission so you can't fight em :(
Instead of running ls for each and every name, you should run it once and store that result into a set (an unordered list - slightly faster). Then check against that when you run check_scene
def check_scene(self, name):
"""Checks entire scene for same name. If duplicate exists,
Keyword Arguments:
name -- (string) name of object to be checked
"""
if not hasattr(self, 'scene'):
self.scene = set(ls())
if name not in self.scene:
return name
else:
return ''