Life in Python

Stato
Discussione chiusa ad ulteriori risposte.

HackLife

Utente Silver
26 Maggio 2008
57
11
0
73
Creator & Manteiner: Hack_Life
Type: Conway's "Game of Life" implementation
Language: Python
Other dependencies: pyGames, files included.
Platform: Windows-tested
Last stable relase: 0.1
License: Creative Commons (follow the link for details)
Link (Source): http://pastebin.com/zakQfi3b
Link (Package): http://localhostr.com/files/5036b4/life.zip
Notes: Implementazione del classico gioco della vita di John Conway. Permette di salvare la configurazione corrente e di caricarne una precedente. Per caricare una configurazione, chiamarla source.lfe e porla nella cartella del programma, poi premere "load" nel programma.

Codice:
# Title:    [   life.py     ]
# Author:   [   HackLife    ]
# Last:     [   0510.10     ]
# Version:  [   0.1         ]
# Licence:  [   CC           ]

import pygame, time, hashlib

class Cell(pygame.sprite.Sprite):
    def __init__(self,gridPos,alive=False):
        pygame.sprite.Sprite.__init__(self)
        self.alive = alive
        self.reloadPic()
        self.rect = self.pic.get_rect()
        self.xAxis = gridPos[0]
        self.yAxis = gridPos[1]
        self.nextGeneration = False
    
    def reverse(self):
        if self.alive:
            self.alive = False
        else:
            self.alive = True
            
        self.reloadPic()

    def generate(self):
        if self.nextGeneration:
            self.live()
        else:
            self.die()
            
    def die(self):
        self.alive = False
        self.reloadPic()

    def live(self):
        self.alive = True
        self.reloadPic()
        
    def reloadPic(self):
        if self.alive:
            self.pic = pygame.image.load('black.png')
        else:
            self.pic = pygame.image.load('blank.png')

    def clickCheck(self,pos):
        if self.rect.collidepoint(pos):
            self.reverse()
            return True
        else:
            return False

    def getNeightbours(self,grid):
        self.neightbours = []
        for cell in grid:
            if cell == self:
                pass
            elif cell.xAxis == self.xAxis or \
            cell.xAxis == self.xAxis+1 or \
            cell.xAxis == self.xAxis-1:
                if cell.yAxis == self.yAxis or \
                cell.yAxis == self.yAxis+1 or \
                cell.yAxis == self.yAxis-1:
                    self.neightbours.append(cell)

    def checkNeightbours(self):
        aliveNeightbours = 0
        for neightbour in self.neightbours:
            if neightbour.alive:
                aliveNeightbours += 1
        if self.alive:
            if 1 < aliveNeightbours < 4:
                self.nextGeneration = True
            else:
                self.nextGeneration = False
        else:
            if aliveNeightbours == 3:
                self.nextGeneration = True
            else:
                self.nextGeneration = False
            

class Button(pygame.sprite.Sprite):
    def __init__(self,pic,pos):
        pygame.sprite.Sprite.__init__(self)
        self.pic = pygame.image.load(pic)
        self.rect = self.pic.get_rect()
        self.rect = self.rect.move(pos)

    def clickCheck(self,pos):
        if self.rect.collidepoint(pos):
            return True
        else:
            return False

class Game:
    def start(self):     
        pygame.init()
        self.width, self.height = (500,400)
        self.screen = pygame.display.set_mode((self.width,self.height))
        self.bStart = Button('b_start.png',(20,340))
        self.bReset = Button('b_reset.png',(120,340))
        self.bNext = Button('b_next.png',(220,340))
        self.bStop = Button('b_stop.png',(320,340))
        self.bSave = Button('b_save.png',(320,180))
        self.bLoad = Button('b_load.png',(320,260))
        self.buttons = [self.bStart,self.bReset,self.bNext,self.bStop,
                        self.bSave,self.bLoad]
        auto = False
        self.epoch = 0
        
        self.makeGrid()

        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    for cell in self.grid:
                        if cell.clickCheck(event.pos):
                            self.updateGrid()
                            break
                    if self.bNext.clickCheck(event.pos):
                        self.next()
                    elif self.bReset.clickCheck(event.pos):
                        self.reset()
                    elif self.bStart.clickCheck(event.pos):
                        auto = True
                    elif self.bStop.clickCheck(event.pos):
                        auto = False
                    elif self.bSave.clickCheck(event.pos):
                        self.save()
                    elif self.bLoad.clickCheck(event.pos):
                        self.load()
                        
            if auto:
                self.next()

    def makeGrid(self):
        print "Loading environment..."
        self.gridSize = 25
        self.grid = []
        for line in range(self.gridSize):
            for cell in range(self.gridSize):
                nCell = Cell((line,cell))
                nCell.rect = nCell.rect.move([line*10,cell*10])
                self.grid.append(nCell)
        for cell in self.grid:
            cell.getNeightbours(self.grid)
        print "Done."
        self.updateGrid()

    def makeCustomGrid(self,source):
        print "Loading configuration..."
        self.grid = []
        iterator = 0
        for line in range(self.gridSize):
            for cell in range(self.gridSize):
                if source[iterator] == '1':
                    nCell = Cell((line,cell),alive=True)
                else:
                    nCell = Cell((line,cell))
                iterator += 1
                nCell.rect = nCell.rect.move([line*10,cell*10])
                self.grid.append(nCell)
        for cell in self.grid:
            cell.getNeightbours(self.grid)
        print "Done."
        self.updateGrid()

    def updateGrid(self):
        self.screen.fill((0,0,0))
        for cell in self.grid:
            self.screen.blit(cell.pic,cell.rect)
        for button in self.buttons:
            self.screen.blit(button.pic,button.rect)
        pygame.display.flip()

    def next(self):
        self.epoch += 1
        print self.epoch
        for cell in self.grid:
            cell.checkNeightbours()
        for cell in self.grid:
            cell.generate()
        self.updateGrid()

    def reset(self):
        self.epoch = 0
        print "Epoch reset."
        for cell in self.grid:
            cell.kill()
        self.makeGrid()

    def save(self):
        filename = hashlib.md5()
        filename.update(str(time.localtime()))
        dest = open("%s.lfe"%filename.hexdigest(),'w')
        dest.write("")
        dest.close()
        dest = open("%s.lfe"%filename.hexdigest(),'a')
        for cell in self.grid:
            if cell.alive:
                dest.write('1')
            else:
                dest.write('0')
        print "Configuration saved."
        dest.close()

    def load(self):
        try:
            source = open("source.lfe",'r')
        except:
            print "No source found. Please name your source 'source.lfe'"
            return
        print "Source Loaded."
        self.makeCustomGrid(source.read())
                
if __name__=="__main__":
    myGame = Game()
    myGame.start()
 
peeerò, complimenti, nn mi sarei mai sognato di farlo.

Alcune precisazioni però:

1) sarebbe *assolutamente* da evitare un while True nel costruttore. Fa qualcosa tipo:
Codice:
tmp = Game()
tmp.start()
che è cosa buona e giusta e molto + oop di quello che fai tu

2) visto che in questo script hai fornito, giustamente direi, alcune classi, devi cmq anche supporre che qualcuno potrebbe voler importare il modulo, magari per ereditarle e/o reimplementarne parti, quindi *in questo specifico caso* è bene, anzi meglio, mettere il
Codice:
if __name__=="__main__":
prima della direttiva che fa partire il gioco.
 
Grazie Malex ;)
Dopo che mi hai headshottato l'ultima volta che avevo messo __name__=="__main__" .. xD
Giusto per curiosità, come mai non vanno i while True nel costruttore?
Per caso hai qualche idea per aumentare l'efficenza del calcolo? Perché è molto lento. Quella che vedi se provi il gioco è la velocità effettiva di calcolo, non c'è nessun time.sleep() o simili...
 
HackLife ha detto:
Grazie Malex ;)
Dopo che mi hai headshottato l'ultima volta che avevo messo __name__=="__main__" .. xD
Giusto per curiosità, come mai non vanno i while True nel costruttore?
Per caso hai qualche idea per aumentare l'efficenza del calcolo? Perché è molto lento. Quella che vedi se provi il gioco è la velocità effettiva di calcolo, non c'è nessun time.sleep() o simili...

i while True non vanno nel costruttore perché *per definizione* il costruttore serve ad istanziare l'oggetto, ogni altro utilizzo deve essere delegato a metodi adatti. I vantaggi?
1) potresti voler modificare al volo un campo dell'oggetto prima della sua esecuzione, cosa che ora nn puoi fare
2) nel caso tu volessi modificare l'esecuzione, o ereditare la classe e reimplementare l'esecuzione, dovresti riscrivere tutto il costruttore, mentre come ho detto io devi solo riscrivere il metodo. Inoltre, sempre se erediti, e reimplementi un altro costruttore, ora come ora poi dovresti richiamare, per far partire il programma, il costruttore padre, cosa che molto probabilmente non vorrai fare. col nuovo sistema invece ti pasterà parent.method

Cmq ti headshootai perché, come molti, avevi fatto una cosa del tipo:
Codice:
def main():
    #some code

if __name__=="__main__":
nel caso mostrato non ha senso mettere in una funzione ed eseguirla, perché il codice NON deve essere importato. Nel caso del tuo programma, tu fornisci anche classi non strettamente dipendenti dall'esecuzione, quindi devi rendere possibile un eventuale importazione.
:rulezmg:
 
Stato
Discussione chiusa ad ulteriori risposte.