Re: [Tutor] Chutes & Ladders

2013-12-23 Thread Steven D'Aprano
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

2013-12-23 Thread Mark Lawrence

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

2013-12-23 Thread Peter Otten
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

2013-12-23 Thread Peter Otten
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

2013-12-23 Thread Keith Winston
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

2013-12-22 Thread Keith Winston
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

2013-12-22 Thread Peter Otten
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

2013-12-22 Thread Keith Winston
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