Re: [Tutor] Re: The Game of Life

2005-01-06 Thread Kent Johnson
Brian van den Broek wrote:
In an earlier version, instead of the run_world() method I now have, I
put the following within my class definition and after (i.e. outside of)
the method defs:
.>while self.current_generation > self.total_generations:
.>time.sleep(self.sleep_interval)
.>self.update_world()
.>self.print_world()
which caused this:
Traceback (most recent call last):
   File "D:\Python Files\foogame_of_life.py", line 3, in -toplevel-
 class life_world:
   File "D:\Python Files\foogame_of_life.py", line 76, in life_world
 while self.current_generation > self.total_generations:
NameError: name 'self' is not defined
That surprised me -- I'd have guessed that within a class, self was
everywhere defined, and not just within methods.
self is just another method parameter. OK not quite, but it is a method parameter and it is only 
bound within the scope of the method. There is nothing magic about the name, either; it is just a 
(strong) convention.

In fact, by using a different name for self and the magic of lexical scoping and closures, you can 
do something very much like Java inner classes - make a nested class that has access to all the 
attributes of the enclosing class.

This is off-topic and a bit twisted, but I'm not going to let that stop me:
''' Make a nested class that has access to the attributes of its parent class 
'''
class Outer:
def __init__(outerSelf, x):
outerSelf.x = x
def makeNested(outerSelf, x):
class Nested:
def __init__(innerSelf, x):
innerSelf.x = x
def showX(innerSelf):
print 'outer x is', outerSelf.x
print 'inner x is', innerSelf.x
return Nested(x)
o = Outer(3)
n = o.makeNested(5)
n.showX()
o.x = 22
n.showX()

prints:
outer x is 3
inner x is 5
outer x is 22
inner x is 5
Kent
Clearly, I'm confused about something.
Anyway, thanks for reading. Best to all,
Brian vdB

# pylife.py
# Version 0.1
# Brian van den Broek
# [EMAIL PROTECTED]
# 2005-01-06 Thursday 17:41
# Copyright 2005
'''An OOP and ASCII based representation of Conway's Game of Life.
Much of the code was inspired by a post of Danny Yoo's on 2005-01-03 04:11
to the Python Tutor List. (You can find that post by searching the archives at
.)
I make no claims to ellegance or efficency -- this is only the second OOP I
have written.'''
import random, time
class life_world:

def __init__(self, X, Y, sleep_interval = 0.5, total_generations = 20):
self.X = X
self.Y = Y
self.world = self.seed_world()
self.sleep_interval = sleep_interval
self.total_generations = total_generations
self.current_generation = 0
self.print_world()

def seed_world(self):
'''Constructs a new random world of size XxY.'''

world = {}
for j in range(self.X):
for i in range(self.Y):
world[i, j] = random.choice((True, False))
return world
def print_world(self):
'''Prints out a string representation of a world.'''
print '\n\nGeneration Number: %s\n' %self.current_generation
print '--'*(self.Y + 1) + '-'
for j in range(self.X):
print '|',
for i in range(self.Y):
if self.world[i, j]:
print 'X',
else:
print ' ',
print '|'
print '--'*(self.Y + 1) + '-'

def count_neighbours(self, cell):
'''Returns the number of live neighbours to this one.

'neghboUrs because I'm Canadian, eh.
'''
live_count = 0
i,j = cell
for i_delta in [-1, 0, 1]:
for j_delta in [-1, 0, 1]:
if (i_delta, j_delta) == (0, 0):
continue
try:
# To deal with the edges of the matrix, where the
# deltas can take us out of bounds.
if self.world[i+i_delta, j+j_delta]:
live_count += 1
except KeyError:
pass
return live_count
def cell_will_change(self, cell):
'''Returns True if a cell will change, False otherwise.'''
change = False
if self.world[cell] and not self.count_neighbours(cell) in (2,3):
change = True
if not self.world[cell] and self.count_neighbours(cell) == 3:
change = True
return change
def get_changed_cells_list(self):
'''Returns a list of cells that will change in the next generation.'''
changed_cells_list = []
for c in self.world:
if self.cell_will_change(c):
changed_cells_list.append(c)
return changed_cells_list
def update_world(self):
'''Produces the n

[Tutor] Re: The Game of Life

2005-01-06 Thread Brian van den Broek
Hi all,
after making the sketch I posted a bit ago, I tried to turn to actual
work. But, the bug had bit. ;-) So, here is an attempt at filling out
that sketch in an OOP way. (It is my second OOP program, so if anyone
was so inclined, I'd very much appreciate any comments. Also, I have a 
question about a snag I ran into while debugging.)

I'm also not too used to sharing complete code. I'd put it in the public
domain, but since so much of it was derived from Danny's code, I don't
really know the conventions for so doing. (Were I to know it
appropriate, I'd follow the copyright comment line with:
# This software is released into the public domain.
# Fold, spindle, or mutilate at will
I've attached it rather than pasted it, as with something >100 lines I
don't feel up to shortening line for email. Sorry 'bout that.
The question:
In an earlier version, instead of the run_world() method I now have, I
put the following within my class definition and after (i.e. outside of)
the method defs:
.>while self.current_generation > self.total_generations:
.>time.sleep(self.sleep_interval)
.>self.update_world()
.>self.print_world()
which caused this:
Traceback (most recent call last):
   File "D:\Python Files\foogame_of_life.py", line 3, in -toplevel-
 class life_world:
   File "D:\Python Files\foogame_of_life.py", line 76, in life_world
 while self.current_generation > self.total_generations:
NameError: name 'self' is not defined
That surprised me -- I'd have guessed that within a class, self was
everywhere defined, and not just within methods.
Clearly, I'm confused about something.
Anyway, thanks for reading. Best to all,
Brian vdB
# pylife.py
# Version 0.1
# Brian van den Broek
# [EMAIL PROTECTED]
# 2005-01-06 Thursday 17:41
# Copyright 2005

'''An OOP and ASCII based representation of Conway's Game of Life.
Much of the code was inspired by a post of Danny Yoo's on 2005-01-03 04:11
to the Python Tutor List. (You can find that post by searching the archives at
.)

I make no claims to ellegance or efficency -- this is only the second OOP I
have written.'''

import random, time

class life_world:

def __init__(self, X, Y, sleep_interval = 0.5, total_generations = 20):
self.X = X
self.Y = Y
self.world = self.seed_world()
self.sleep_interval = sleep_interval
self.total_generations = total_generations
self.current_generation = 0
self.print_world()

def seed_world(self):
'''Constructs a new random world of size XxY.'''

world = {}
for j in range(self.X):
for i in range(self.Y):
world[i, j] = random.choice((True, False))
return world

def print_world(self):
'''Prints out a string representation of a world.'''

print '\n\nGeneration Number: %s\n' %self.current_generation
print '--'*(self.Y + 1) + '-'
for j in range(self.X):
print '|',
for i in range(self.Y):
if self.world[i, j]:
print 'X',
else:
print ' ',
print '|'
print '--'*(self.Y + 1) + '-'

def count_neighbours(self, cell):
'''Returns the number of live neighbours to this one.

'neghboUrs because I'm Canadian, eh.
'''
live_count = 0
i,j = cell
for i_delta in [-1, 0, 1]:
for j_delta in [-1, 0, 1]:
if (i_delta, j_delta) == (0, 0):
continue
try:
# To deal with the edges of the matrix, where the
# deltas can take us out of bounds.
if self.world[i+i_delta, j+j_delta]:
live_count += 1
except KeyError:
pass
return live_count

def cell_will_change(self, cell):
'''Returns True if a cell will change, False otherwise.'''
change = False
if self.world[cell] and not self.count_neighbours(cell) in (2,3):
change = True
if not self.world[cell] and self.count_neighbours(cell) == 3:
change = True
return change

def get_changed_cells_list(self):
'''Returns a list of cells that will change in the next generation.'''

changed_cells_list = []
for c in self.world:
if self.cell_will_change(c):
changed_cells_list.append(c)
return changed_cells_list

def update_world(self):
'''Produces the next generation world.'''
self.current_generation += 1
changed_cells_list = self.get_changed_cells_list()
for c in changed_cells_list:
self.world[c] = not self.world[c]

def run_world(self):
while self.current_generation < self.total_generations:
time.sleep(self.sleep_interval)
self.upd

Re: [Tutor] Re: The Game of Life

2005-01-06 Thread Max Noel
On Jan 6, 2005, at 21:20, Brian van den Broek wrote:
Oh, the Life rules allow a world where every cell will change in 
the next generation, iff your world is a torus (i.e. the lower row 
"touches" the upper row as if it were immediately above it, and the 
right column "touches" the left column as if it were immediately left 
of it). It is quite trivial: set all cells to LIVE. Next generation 
they're all DEAD.
Topologist! (That's cheating!) ;-)
If we are going that way, you 'iff' seems a bit hasty. Take the 1x1 
matrix 'full' of live cells.
	Well, if the only cell of a 1x1 torus matrix is LIVE, that  means it 
is surrounded by 4 LIVE cells, doesn't it? :D

Also, other 'funny' (in the sense that a torus is funny) planes could 
be defined (say a torus-like structure with more than 1 whole -- 
cannot recall the general terminology from ill-remembered topology), 
etc. I meant the claim for a standard non-trivial (i.e. M > 1 and N > 
1) MxN euclidean plane matrix, but your correction is both amusing and 
welcome.
	Thanks :)
	However, the main reason why I talked about a torus is that it's one 
of the two obvious choices when you're implementing Life using a 2D 
matrix (the other being a finite rectangular plane).

-- Max
maxnoel_fr at yahoo dot fr -- ICQ #85274019
"Look at you hacker... A pathetic creature of meat and bone, panting 
and sweating as you run through my corridors... How can you challenge a 
perfect, immortal machine?"

___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Re: The Game of Life

2005-01-06 Thread Brian van den Broek
Max Noel said unto the world upon 2005-01-06 15:39:

On Jan 6, 2005, at 20:05, Brian van den Broek wrote:

I gave some thought (though produced no code) to the question of how 
to do a life game before you [Danny] posted your code. My naive approach 
differs a bit, and it seems to me better. I'd like to know why I am 
wrong ;-)

So, relying on your [Danny's] code unless differences specified (and, like you, 
not making it OOP, so having more passing of the world than I'd like, 
but I'm only just now groking the OOP way), I would have done 
something like the following. (Please be tolerant of any syntactic 
slips; I think and hope my aim is clear, if not the execution.):

1) Define world by filling an M by N matrix with True and False. 
Otherwise, as your step (1).

2) Define a print function which goes through the matrix, printing one 
thing for each True cell and another for each False cell.

3) Define the count_live_neighbours() roughly as you did. (The actual 
code below assumes your count function, modulo the change to it that 
you define a LIVE name and I am just using True.)

4) Define a "will be changed function" roughly as the untested code:


5) Define a get_changed_cells_list function (again, untested):


6) Define update_world function (again, untested):

I hope the fragmentary nature of this makes my intent clear.
Anyway, as I see it, this has the following advantages over your 
posted code:

1) Cell representations done with built-ins seems likely to be 
quicker. (A minor point, though.)

2) Use of booleans for cell contents makes the change cell procedure 
simply saying not
.> cell_contents  # as in for loop of my update_world().

3) Neither a copy of the world nor the design pattern is needed. 
Instead, I make a list of the cells to be changed. In the limit, where 
ever cell will change, this is no better than a copy of the world, but 
i) it often is better, and ii) I'm not sure the Life rules can create 
a world where every cell will change in the next generation, anyway.

I don't see any disadvantages, but then I don't see too well ;-)

You're wrong in that you're not wrong. I'm not very familiar with 
design patterns yet, but to me, what you just described looks like 
another Life implementation using the Command design pattern. It is, 
however, more efficient and IMO elegant than Danny's.
Well, thanks. :-)
Correct me if I'm wrong (I may be talking out of, er, a certain part 
of my anatomy that is far from my mouth), but the basic idea of the 
Command design pattern is to store the changes to be made (instead of 
the post-change states) in a disposable data structure, then to apply 
them to the original copy of the world. Which should be more efficient, 
at least memory-wise. Right?

I don't know design patterns beyond the concept and a teeny bit of 
poking about. I guess I should check, but I got the sense that the 
Command pattern involved storing actual instructions instead of 
representational data. (Ugly term, but since Python functions are 
first-class objects I need something for data that isn't an 
instruction.) The sense comes more from the name than anything else, though.

Oh, the Life rules allow a world where every cell will change in the 
next generation, iff your world is a torus (i.e. the lower row "touches" 
the upper row as if it were immediately above it, and the right column 
"touches" the left column as if it were immediately left of it). It is 
quite trivial: set all cells to LIVE. Next generation they're all DEAD.
Topologist! (That's cheating!) ;-)
If we are going that way, you 'iff' seems a bit hasty. Take the 1x1 
matrix 'full' of live cells. Also, other 'funny' (in the sense that a 
torus is funny) planes could be defined (say a torus-like structure with 
more than 1 whole -- cannot recall the general terminology from 
ill-remembered topology), etc. I meant the claim for a standard 
non-trivial (i.e. M > 1 and N > 1) MxN euclidean plane matrix, but your 
correction is both amusing and welcome.


Best to all,
Brian vdB
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Re: The Game of Life

2005-01-06 Thread Max Noel
	First of all, thanks for answering our questions, Danny! And sorry for 
the lag before my reply, but I was rather busy over the last few days 
(moving "back" to the UK).

On Jan 6, 2005, at 20:05, Brian van den Broek wrote:
I am having a hard time figuring out how to efficiently snip and 
comment, so please bear with any organizational issues. I think I will 
just preserve the parts relevant to what I want to talk/ask about and 
write at the end.
And I hereby massively SNIP whatever remained of it. :D
I gave some thought (though produced no code) to the question of how 
to do a life game before you posted your code. My naive approach 
differs a bit, and it seems to me better. I'd like to know why I am 
wrong ;-)

So, relying on your code unless differences specified (and, like you, 
not making it OOP, so having more passing of the world than I'd like, 
but I'm only just now groking the OOP way), I would have done 
something like the following. (Please be tolerant of any syntactic 
slips; I think and hope my aim is clear, if not the execution.):

1) Define world by filling an M by N matrix with True and False. 
Otherwise, as your step (1).

2) Define a print function which goes through the matrix, printing one 
thing for each True cell and another for each False cell.

3) Define the count_live_neighbours() roughly as you did. (The actual 
code below assumes your count function, modulo the change to it that 
you define a LIVE name and I am just using True.)

4) Define a "will be changed function" roughly as the untested code:

5) Define a get_changed_cells_list function (again, untested):

6) Define update_world function (again, untested):

I hope the fragmentary nature of this makes my intent clear.
Anyway, as I see it, this has the following advantages over your 
posted code:

1) Cell representations done with built-ins seems likely to be 
quicker. (A minor point, though.)

2) Use of booleans for cell contents makes the change cell procedure 
simply saying not
.> cell_contents  # as in for loop of my update_world().

3) Neither a copy of the world nor the design pattern is needed. 
Instead, I make a list of the cells to be changed. In the limit, where 
ever cell will change, this is no better than a copy of the world, but 
i) it often is better, and ii) I'm not sure the Life rules can create 
a world where every cell will change in the next generation, anyway.

I don't see any disadvantages, but then I don't see too well ;-)
	You're wrong in that you're not wrong. I'm not very familiar with 
design patterns yet, but to me, what you just described looks like 
another Life implementation using the Command design pattern. It is, 
however, more efficient and IMO elegant than Danny's.
	Correct me if I'm wrong (I may be talking out of, er, a certain part 
of my anatomy that is far from my mouth), but the basic idea of the 
Command design pattern is to store the changes to be made (instead of 
the post-change states) in a disposable data structure, then to apply 
them to the original copy of the world. Which should be more efficient, 
at least memory-wise. Right?

	Oh, the Life rules allow a world where every cell will change in the 
next generation, iff your world is a torus (i.e. the lower row 
"touches" the upper row as if it were immediately above it, and the 
right column "touches" the left column as if it were immediately left 
of it). It is quite trivial: set all cells to LIVE. Next generation 
they're all DEAD.
	If your world is a finite rectangle, I think the best you can get is a 
world where all but four cells (the corners) change next generation 
(same method). As your rectangle stretches to infinity, obviously, the 
cells changed/total cells ration converges toward 1.

In any case, your point still stands.
did you mean make the world a class (and thus avoid the sort of 
passing the world dict up and down as I did here?) Either way, a quick 
word or two will be great; I'm not asking for you to take the time to 
code it up for me :-)
Probably, but I don't think that's relevant.
-- Max
maxnoel_fr at yahoo dot fr -- ICQ #85274019
"Look at you hacker... A pathetic creature of meat and bone, panting 
and sweating as you run through my corridors... How can you challenge a 
perfect, immortal machine?"

___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


[Tutor] Re: The Game of Life

2005-01-06 Thread Brian van den Broek
Danny Yoo said unto the world upon 2005-01-03 04:11:
On Mon, 3 Jan 2005, Brian van den Broek wrote:

(Aside: one nonobvious example where copying can be avoided is in
Conway's Game of Life:  when we calculate what cells live and die in
the next generation, we can actually use the 'Command' design pattern
to avoid making a temporary copy of the world.  We can talk about
this in more detail if anyone is interested.)

Seconded. (Thanks for offering, Danny.)

[Long post up ahead.]
Just as a warning, none of what I'm going to code here is original at all:
I'm rehashing a main idea off of a paper called "Using the Game of Life to
Introduce Freshman Students to the Power and Elegance of Design Patterns":
http://portal.acm.org/citation.cfm?id=1035292.1028706
Hi Danny and all,
Thanks for doing this, Danny. (And sorry to have taken a bit to get to it.)

As an aside, Daniel Dennett, a philosopher who seems popular amongst the 
hacker-types, wrote some about Life some time in the 80's, I believe in 
the first instance in a paper called "Real Patterns". One of the neat 
things he toys with there is the (needless to say, abstract) possibility 
of instantiating a Turing-Complete computer out of the Life cells.

Similarly, Wolfram's recent tome _A New Kind of Science_, from reviews 
and the skimming I've done, seems largely devoted to the idea that such 
cellular automata are a rich and untapped source of insight and 
scientific modelling. Wolfram's book is on my list, but, sadly, 1000'ish 
page books have a tendency to remain on that list ;-)


OK, Danny, I get that you were quickly (and kindly) adapting an approach 
from somewhere else. So, this is all by way of query rather than criticism.

I am having a hard time figuring out how to efficiently snip and 
comment, so please bear with any organizational issues. I think I will 
just preserve the parts relevant to what I want to talk/ask about and 
write at the end.

Ok, let's start hacking.  The Game of Life involves a 2-D matrix of cells,
where any cell can be 'LIVE' or 'DEAD'.
###
LIVE, DEAD = '*', '.'
###

def make_random_world(M, N):
"""Constructs a new random game world of size MxN."""
world = {}
for j in range(N):
for i in range(M):
world[i, j] = random.choice([LIVE, DEAD])
world['dimensions'] = (M, N)
return world
def print_world(world):
"""Prints out a string representation of a world."""
M, N = world['dimensions']
for j in range(N):
for i in range(M):
print world[i, j],
print
###
(If I had more space, I'd rather do this as a real data structure instead
of hacking up a dictionary.)

So now we have something that shows a single generation of a game world.
How does the world run?  Well, between each generation, each cell can
either live or die according to the rule:
A dead cell with exactly 3 live neighbors becomes alive (or is
"born").
A live cell with 2 or 3 live neighbors stays alive; otherwise it dies
(from "loneliness").

###
def get_state(world, i, j):
"""Returns the state of the cell at position (i, j).  If we're out
of bounds, just returns DEAD to make calculations easier."""
return world.get((i, j), DEAD)
def count_live_neighbors(world, i, j):
"""Returns the number of live neighbors to this one."""
live_count = 0
for i_delta in [-1, 0, 1]:
for j_delta in [-1, 0, 1]:
if (i_delta, j_delta) == (0, 0):
continue
if get_state(world, i+i_delta, j+j_delta) == LIVE:
live_count += 1
return live_count
def is_born(world, i, j):
"""Returns True if the cell at (i, j) is currently dead, but will
be born in the next generation."""
return (get_state(world, i, j) == DEAD and
count_live_neighbors(world, i ,j) == 3)
def is_deceased(world, i, j):
"""Returns True if the cell at (i, j) is currently alive, but will
die in the next generation."""
return (get_state(world, i, j) == LIVE and
not (2 <= count_live_neighbors(world, i ,j) <= 3))
###

Now the problem is: given the current state of the world, how do we
calculate the next state of the world?  One way is to make a temporary
copy of the world, and apply changes to it, consulting the original
world for the rules:

The unsatisfying thing, though, is that we use an explicit copying step
and the temporary scratch space.  But we can actually get away from making
temporary scratch space by using this cute idea: instead of directly
applying status changes immediately, we store a list of "Commands" that we
can execute after we figure out who lives and who dies:
###
def make_rise_command(world, i, j):
"""Produces a callable function that, when called, will apply a change
to the world to make the cell at (i, j) live."""
def cmd():
world[i, j] = LIVE
return cmd
def make_die_command(world, i, j):
"""Produces a callable function that, when called, will apply a change
to t