Re: [Tutor] Chutes & Ladders
On Mon, Dec 23, 2013 at 01:07:15AM -0500, Keith Winston wrote: > On Sun, Dec 22, 2013 at 3:04 PM, wrote: > > > > > games = [p1.gameset(gamecount) for _ in range(multicount)] [...] > But in my haste I included the line above, cut & pasted from his > suggestion, without understanding what the underscore is doing in there. It's just a name. Names in Python can include alphanumeric characters and the underscore, and they can start with an underscore. There is a convention to use _ as a "don't care" name for variables which aren't used. Some languages have a loop like: repeat 5 times: ... where there is no loop variable. Python isn't one of those languages, so the closest is to flag the loop variable as something we don't care about. > I think I understand that at the prompt of the interpreter the underscore > returns the last value returned... Correct. That's another convention for the single underscore. There's a third: _() as a function is used for translating strings from one language (say, English) to another language (say, German). > but I can't really figure out what it's > doing here. > > Also: this statement worked fine: > > for tmulti in games: > print("{moves:9.2f} {chutes:12.2f} {ladders:13.2f}".format( > moves=mean(tgset[1] for tgset in tmulti), > chutes=mean(tgset[2] for tgset in tmulti), > ladders=mean(tgset[3] for tgset in tmulti) > )) > > Which is sort of awesome to me, but in my efforts to riff on it I've been > stumped: Break it up into pieces: template = "{moves:9.2f} {chutes:12.2f} {ladders:13.2f}" m = mean(tgset[1] for tgset in tmulti) c = mean(tgset[2] for tgset in tmulti) l = mean(tgset[3] for tgset in tmulti) message = template.format(moves=m, chutes=c, ladders=l) print(message) > if I want to further process the arrays/lists my list > comprehensions are generating, I sort of can't, since they're gone: that > is, if I wanted to use the entire len(tmulti) of them to determine grand > total averages and variances, I can't. I'm not entirely sure I understand what you are trying to say, but you can always save your list comprehension, then calculate with it: moves = [tgset[1] for tgset in tmulti] average_moves = mean(moves) variance_moves = variance(moves) > And if my array is large, I woudn't > want to iterate over it over and over to do each of these steps, I think. True, but I think that your idea of "large" is probably not so very large compared to what the computer considers large. If speed is an issue, you can calculate both the mean and variance with a single pass over the data (although potentially with a loss of accuracy). -- Steven ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Chutes & Ladders
On 23/12/2013 06:07, Keith Winston wrote: On Sun, Dec 22, 2013 at 3:04 PM, mailto:tutor-requ...@python.org>> wrote: games = [p1.gameset(gamecount) for _ in range(multicount)] So Peter gave me a list of suggesttions, all of which I've incorporated and gotten running, and it's been instructive. But in my haste I included the line above, cut & pasted from his suggestion, without understanding what the underscore is doing in there. I think I understand that at the prompt of the interpreter the underscore returns the last value returned... but I can't really figure out what it's doing here. Also: this statement worked fine: for tmulti in games: print("{moves:9.2f} {chutes:12.2f} {ladders:13.2f}".format( moves=mean(tgset[1] for tgset in tmulti), chutes=mean(tgset[2] for tgset in tmulti), ladders=mean(tgset[3] for tgset in tmulti) )) Which is sort of awesome to me, but in my efforts to riff on it I've been stumped: if I want to further process the arrays/lists my list comprehensions are generating, I sort of can't, since they're gone: that is, if I wanted to use the entire len(tmulti) of them to determine grand total averages and variances, I can't. And if my array is large, I woudn't want to iterate over it over and over to do each of these steps, I think. Anyway, I'm just goofing on all this, for the learning value, but I'll appreciate any thoughts. Thanks. I'm also trying to speed up the program at this point: the way I've set it up now it builds a list of lists (of lists) of all the stats of all the games of all the gamesets of all the multisets. I've visually streamlined it some, without any effect on performance I can detect. Would using arrays, or tuples, or something else be likely to be faster? It's interesting that running it on my 8 y.o. Core 2 Duo and my 2 y.o. Core I5 result in virtually exactly the same speed. Weird. Obviously, it's just doing simple math, pretty much -- Keith Maybe this helps https://wiki.python.org/moin/PythonSpeed/PerformanceTips ? -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Chutes & Ladders
Keith Winston wrote: > On Sun, Dec 22, 2013 at 3:04 PM, wrote: > >> >> games = [p1.gameset(gamecount) for _ in range(multicount)] > > > So Peter gave me a list of suggesttions, all of which I've incorporated > and gotten running, and it's been instructive. > > But in my haste I included the line above, cut & pasted from his > suggestion, without understanding what the underscore is doing in there. I > think I understand that at the prompt of the interpreter the underscore > returns the last value returned... but I can't really figure out what it's > doing here. There is a convention (recognized by pylint) to have unused variables start with an underscore. The shortest of these names is just the underscore. I used it to indicate that the important thing is that gameset() is called, not whether it is the seventh or 42nd call. Following that convention a line counting script could look linecount = 0 for _line in sys.stdin: linecount += 1 print(linecount, "lines") while a script for adding line numbers would be lineno = 1 for line in sys.stdin print("{:5}:".format(lineno), line, end="") lineno += 1 > Also: this statement worked fine: > > for tmulti in games: > print("{moves:9.2f} {chutes:12.2f} {ladders:13.2f}".format( > moves=mean(tgset[1] for tgset in tmulti), > chutes=mean(tgset[2] for tgset in tmulti), > ladders=mean(tgset[3] for tgset in tmulti) > )) > > Which is sort of awesome to me, but in my efforts to riff on it I've been > stumped: if I want to further process the arrays/lists my list > comprehensions are generating, I sort of can't, since they're gone: that > is, if I wanted to use the entire len(tmulti) of them to determine grand > total averages and variances, I can't. And if my array is large, I woudn't > want to iterate over it over and over to do each of these steps, I think. > Anyway, I'm just goofing on all this, for the learning value, but I'll > appreciate any thoughts. Thanks. You can modify the loop to for tmulti in games: moves = [tgset[1] for tgset in tmulti] chutes = ... ladders = ... print("...".format( moves=mean(moves), chutes=mean(chutes), ladders=mean(ladders) )) > I'm also trying to speed up the program at this point: the way I've set it > up now it builds a list of lists (of lists) of all the stats of all the > games of all the gamesets of all the multisets. I've visually streamlined > it some, without any effect on performance I can detect. Would using > arrays, or tuples, or something else be likely to be faster? It's > interesting that running it on my 8 y.o. Core 2 Duo and my 2 y.o. Core I5 > result in virtually exactly the same speed. Weird. Obviously, it's just > doing simple math, pretty much "Simple math" is where the overhead of CPython over languages like C is most significant. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Chutes & Ladders
Keith Winston wrote: > On Sun, Dec 22, 2013 at 3:04 PM, wrote: > >> To sum it up: I like what you have, my hints are all about very minor >> points >> :) >> > > > Peter, that's a bunch of great suggestions, I knew there were a lot of > places to streamline, make more readable, and probably make faster. Thank > you. > > I find that if I run it [1] [10] times, it takes about 20s on my > machine, so I'm eventually going to take a little time seeing how fast I > can get it, I think... maybe I'll learn decorators so I can time it/parts > of it? > > I'm really grateful for yours and everyone's help. I don't expect any of my suggestions to have a significant impact on execution speed. If you are looking for a cheap way to make your script a bit faster try replacing roll = random.randint(1,6) with roll = int(random.random() * 6) + 1 Further speedups may be gained from undoing (some of the) OO... ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Chutes & Ladders
On Sun, Dec 22, 2013 at 3:04 PM, wrote: > > games = [p1.gameset(gamecount) for _ in range(multicount)] So Peter gave me a list of suggesttions, all of which I've incorporated and gotten running, and it's been instructive. But in my haste I included the line above, cut & pasted from his suggestion, without understanding what the underscore is doing in there. I think I understand that at the prompt of the interpreter the underscore returns the last value returned... but I can't really figure out what it's doing here. Also: this statement worked fine: for tmulti in games: print("{moves:9.2f} {chutes:12.2f} {ladders:13.2f}".format( moves=mean(tgset[1] for tgset in tmulti), chutes=mean(tgset[2] for tgset in tmulti), ladders=mean(tgset[3] for tgset in tmulti) )) Which is sort of awesome to me, but in my efforts to riff on it I've been stumped: if I want to further process the arrays/lists my list comprehensions are generating, I sort of can't, since they're gone: that is, if I wanted to use the entire len(tmulti) of them to determine grand total averages and variances, I can't. And if my array is large, I woudn't want to iterate over it over and over to do each of these steps, I think. Anyway, I'm just goofing on all this, for the learning value, but I'll appreciate any thoughts. Thanks. I'm also trying to speed up the program at this point: the way I've set it up now it builds a list of lists (of lists) of all the stats of all the games of all the gamesets of all the multisets. I've visually streamlined it some, without any effect on performance I can detect. Would using arrays, or tuples, or something else be likely to be faster? It's interesting that running it on my 8 y.o. Core 2 Duo and my 2 y.o. Core I5 result in virtually exactly the same speed. Weird. Obviously, it's just doing simple math, pretty much -- Keith ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Chutes & Ladders
On Sun, Dec 22, 2013 at 3:04 PM, wrote: > To sum it up: I like what you have, my hints are all about very minor > points > :) > Peter, that's a bunch of great suggestions, I knew there were a lot of places to streamline, make more readable, and probably make faster. Thank you. I find that if I run it [1] [10] times, it takes about 20s on my machine, so I'm eventually going to take a little time seeing how fast I can get it, I think... maybe I'll learn decorators so I can time it/parts of it? I'm really grateful for yours and everyone's help. -- Keith ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Chutes & Ladders
Keith Winston wrote: > I've put together my first small program. It's a simulation of the game > Chutes & Ladders. It plays the game and amasses an array of ([multicount] > [gamecount]) size, and then crunches simple stats on the average moves, > chutes, and ladders for all games in each high-level (multi) pass. > Hopefully the code is clear. > > I don't think I really thought out the OOP element properly... I was > thinking of accomodating a multiplayer version in the future, but that > would require a significant rewrite. Perhaps Games should be a separate > class, I still don't really have OOP down (I'm learning Python, OOP, and > Linux simultaneously). There's no interaction between players in the game, > so there's not really any reason to do a multiplayer version: I was just > using this to familiarize myself with basic Python and maybe some stats. You could add a rule that a second player arriving on a field can kick out the current occupant -- like in http://en.wikipedia.org/wiki/Mensch_ärgere_dich_nicht > I'd be interested in ALL feedback: aesthetic, functional, design, > whatever. I would have used numpy but I couldn't get it installed... I > noticed, > belatedly, that the math module has arrays, I didn't look to see if they > would have made sense to use, I think I remember hearing something about > them being inefficient or something. Anyway, thanks. My random remarks: > #Chutes & Ladders Simulation 1.0 > import random > > # Landing on a chute (key) causes one to slide down to the corresponding > value. > chutes = {16: 6, 47: 26, 49: 11, 56: 53, 62: 19, 64: 60, 87: 24, 93: 73, > 95: 75, 98:78} > > # Landing on a ladder (key) causes one to slide up to the corresponding > value. > ladders = {1: 38, 4: 14, 9: 31, 21: 42, 28: 84, 36: 44, 51: 67, 71: 91, > 80:100} > > class Player: > """Player class for Chutes & Ladders.""" > > def __init__(self): Consider providing chutes and ladders as arguments to avoid the implicit dependency from global variables. > self.reset() > > def reset(self): > self.position = 0 > self.movecount = 0 > self.numchutes = 0 > self.numladders = 0 > > def move(self): > """Single move, with chutes, ladders, & out of bounds""" > roll = random.randint(1,6) > self.movecount += 1 > self.position += roll > if self.position in chutes.keys(): > self.position = chutes.get(self.position) > self.numchutes += 1 .keys() is redundant in Python 3 (and does a lot of extra work in Python 2). When you already know that key is in somedict `somedict[key]` is more efficient than `somedict.get(key)` and has the same result. I would write the above two lines as if self.position in chutes: self.position = chutes[self.position] > elif self.position in ladders.keys(): > self.position = ladders.get(self.position) > self.numladders += 1 When you look at the code you see that chutes and ladders are handled the same way. You might consider using a single dictionary for both. > elif self.position > 100: # move doesn't count, have to land > exactly > self.position -= roll > > def game(self): > """Single game""" > self.reset() > while self.position < 100: > self.move() > > def gameset(self, reps): > """A set of games, returning associated stats array""" > setstats = [] > for i in range(reps): > self.game() > stat = [i, self.movecount, self.numchutes, self.numladders] As you are not planning to append items to `stat` a tuple is more idiomatic here than a list. For extra readability try collections.namedtuple which allows accessing the data in the tuple as (for example) `stat.numchutes` instead of the hard to remember stat[3]. > setstats.append(stat) > return setstats > > def multisets(self, multi, reps): > """A set of game sets, adding another dim to the stats array""" > multistats = [] > for i in range(multi): > set1 = p1.gameset(reps) That should be set1 = self.gameset(reps) > multistats.append(set1) > return multistats I'd probably remove the multisets() method from the class to keep the interface small and replicate the functionality in client code with games = [p1.gameset(gamecount) for _ in range(multicount)] I might do the same for gameset() (and have game() return the statistics for a single game). > p1 = Player() > gamecount = 1000 > multicount = 10 > games = p1.multisets(multicount, gamecount) > print("Avg moves Avg chutes Avg ladders") > for i in range(multicount): > tmulti = games[i] In Python you can iterate over a list directly. With that in mind... > summoves, sumchutes, sumladders = 0, 0, 0 > for j in range(gamecount): > tgset = tmulti[j] > s
[Tutor] Chutes & Ladders
I've put together my first small program. It's a simulation of the game Chutes & Ladders. It plays the game and amasses an array of ([multicount] [gamecount]) size, and then crunches simple stats on the average moves, chutes, and ladders for all games in each high-level (multi) pass. Hopefully the code is clear. I don't think I really thought out the OOP element properly... I was thinking of accomodating a multiplayer version in the future, but that would require a significant rewrite. Perhaps Games should be a separate class, I still don't really have OOP down (I'm learning Python, OOP, and Linux simultaneously). There's no interaction between players in the game, so there's not really any reason to do a multiplayer version: I was just using this to familiarize myself with basic Python and maybe some stats. I'd be interested in ALL feedback: aesthetic, functional, design, whatever. I would have used numpy but I couldn't get it installed... I noticed, belatedly, that the math module has arrays, I didn't look to see if they would have made sense to use, I think I remember hearing something about them being inefficient or something. Anyway, thanks. #Chutes & Ladders Simulation 1.0 import random # Landing on a chute (key) causes one to slide down to the corresponding value. chutes = {16: 6, 47: 26, 49: 11, 56: 53, 62: 19, 64: 60, 87: 24, 93: 73, 95: 75, 98:78} # Landing on a ladder (key) causes one to slide up to the corresponding value. ladders = {1: 38, 4: 14, 9: 31, 21: 42, 28: 84, 36: 44, 51: 67, 71: 91, 80:100} class Player: """Player class for Chutes & Ladders.""" def __init__(self): self.reset() def reset(self): self.position = 0 self.movecount = 0 self.numchutes = 0 self.numladders = 0 def move(self): """Single move, with chutes, ladders, & out of bounds""" roll = random.randint(1,6) self.movecount += 1 self.position += roll if self.position in chutes.keys(): self.position = chutes.get(self.position) self.numchutes += 1 elif self.position in ladders.keys(): self.position = ladders.get(self.position) self.numladders += 1 elif self.position > 100: # move doesn't count, have to land exactly self.position -= roll def game(self): """Single game""" self.reset() while self.position < 100: self.move() def gameset(self, reps): """A set of games, returning associated stats array""" setstats = [] for i in range(reps): self.game() stat = [i, self.movecount, self.numchutes, self.numladders] setstats.append(stat) return setstats def multisets(self, multi, reps): """A set of game sets, adding another dim to the stats array""" multistats = [] for i in range(multi): set1 = p1.gameset(reps) multistats.append(set1) return multistats p1 = Player() gamecount = 1000 multicount = 10 games = p1.multisets(multicount, gamecount) print("Avg moves Avg chutes Avg ladders") for i in range(multicount): tmulti = games[i] summoves, sumchutes, sumladders = 0, 0, 0 for j in range(gamecount): tgset = tmulti[j] summoves += tgset[1] sumchutes += tgset[2] sumladders += tgset[3] print(str(summoves/gamecount).rjust(9), \ str(sumchutes/gamecount).rjust(12), \ str(sumladders/gamecount).rjust(13)) Sample output is Avg moves Avg chutes Avg ladders 38.9074.192 3.368 38.644.173 3.276 39.5844.259 3.355 39.2544.243 3.411 40.434.399 3.378 39.634.195 3.305 38.5044.046 3.301 39.9174.265 3.281 39.6784.317 3.335 39.5854.229 3.326 Thanks for any suggestions or thoughts. I know this is a very simple program, but I'm very pleased that, once I began to sort out the basics, it fell together pretty readily: I really like Python, though there's a lot to learn. FYI, I recently played C & L with a 4 y.o. friend, it is not otherwise my game of choice ;-) -- Keith ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor