Re: [Tutor] Re: The Game of Life
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
[Tutor] Re: The Game of Life
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 http://mail.python.org/pipermail/tutor/.) 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.update_world()
Re: [Tutor] Re: The Game of Life
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 http://mail.python.org/pipermail/tutor/.) 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