Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Collection extend [
- incAt: key [ ^self at: key put: (self at: key ifAbsent: [0]) + 1 ]
- ]
- "
- | Class to handle the geometry of the space for our life automaton.
- | Must be subclassed to be used. The jobs of the subclass:
- | - mark the initial cells that are alive via setAlive/flip
- | - define the geometry by overriding the neighbours method
- "
- Object subclass: Space [
- | liveCells currActive nextActive changes |
- Space class >> new [
- ^(super new) init
- ]
- init [
- liveCells := Set new.
- changes := 0.
- ^self
- ]
- " Geometry definition method: "
- neighbours: cellCoord [
- self subclassResponsibility
- ]
- " State change methods: "
- setAlive: cellCoord [
- " Returns if cell is alive after"
- liveCells add: cellCoord.
- changes := changes + 1.
- ^true
- ]
- setDead: cellCoord [
- " Returns if cell is alive after"
- liveCells remove: cellCoord ifAbsent: [].
- changes := changes + 1.
- ^false
- ]
- flipCell: cellCoord [
- ^(self isAlive: cellCoord) ifTrue: [ self setDead: cellCoord ]
- ifFalse: [ self setAlive: cellCoord ].
- ]
- " Functions to track generation state: "
- initState [
- " Note: this is moved to currActive by first advanceTurn call "
- nextActive := Dictionary new.
- " Initialize live counts to nextActive "
- liveCells do: [ :coord | self markActive: coord ].
- ]
- advanceTurn [
- changes := 0.
- currActive := nextActive.
- nextActive := Dictionary new.
- ]
- markActive: coord [
- " NOTE: Assumes cell is alive, adds one to neighbours for next turn "
- nextActive at: coord ifAbsentPut: [0].
- (self neighbours: coord) do: [:neigh | nextActive incAt: neigh].
- ]
- " Access methods: "
- liveCells [ ^liveCells ]
- isAlive: cellCoord [
- ^liveCells includes: cellCoord
- ]
- changes [
- ^changes
- ]
- numActive [
- ^nextActive size
- ]
- currActive [
- ^currActive
- ]
- ]
- "
- | Class to run a simple cellular automaton.
- | space: Subclass of above class, defines neighbour geometry
- | dieRule: Takes # of live neighbours, returns if live cell would die
- | liveRule: Takes # of live neighbours, returns if dead cell would become live
- "
- Object subclass: Automaton [
- | space dieRule liveRule format |
- " Constructors: "
- Automaton class >> space: aSpace dieRule: aBlock1 liveRule: aBlock2 [
- ^(super new) init: aSpace dies: aBlock1 lives: aBlock2 format: #table
- ]
- Automaton class >> space: aSpace dieRule: aBlock1 liveRule: aBlock2 format: aSym [
- ^(super new) init: aSpace dies: aBlock1 lives: aBlock2 format: aSym
- ]
- init: aSpace dies: aBlock1 lives: aBlock2 format: aSym [
- space := aSpace.
- dieRule := aBlock1.
- liveRule := aBlock2.
- format := aSym.
- ^self
- ]
- outputStatus: turn [
- (('Turn: %1 @Live: %2 @Active: %3 @Changes: %4 @Time: %5 ' %
- {
- turn.
- space liveCells size.
- space numActive.
- space changes.
- Time millisecondClock / 1000.0
- }) replaceAll: $@ with: Character tab) display.
- (format = #line) ifTrue: [ stdout cr; flush ]
- ifFalse: [ stdout nl; flush ].
- ]
- statusFormat: formSym [
- " formSym should be one of #none, #line, or #table "
- ^format := formSym.
- ]
- " Private method (do not call): "
- runOneTurn: updateNextTurn [
- | notLastTurn |
- space advanceTurn.
- space currActive keysAndValuesDo: [ :coord :liveNeigh |
- | alive |
- (alive := space isAlive: coord) ifTrue: [
- " Alive cell case: check if dies "
- (dieRule value: liveNeigh) ifTrue: [
- alive := space setDead: coord.
- ]
- ] ifFalse: [
- " Dead cell case: check if becomes alive "
- (liveRule value: liveNeigh) ifTrue: [
- alive := space setAlive: coord.
- ]
- ].
- (alive and: [updateNextTurn]) ifTrue: [ space markActive: coord ]
- ].
- ]
- " Returns a Generator stream for turns of the automaton "
- getTurnGenerator [
- ^Generator on: [ :gen |
- | turn |
- " Initialize active cells (also counts live neighbours): "
- space initState.
- turn := 1.
- [
- self runOneTurn: true.
- (format ~= #none) ifTrue: [ self outputStatus: turn ].
- gen yield: space.
- turn := turn + 1.
- ] repeat
- ]
- ]
- " Cover method for generator that runs automaton until no changes "
- runUntilStable [
- | gen |
- gen := self getTurnGenerator.
- [ gen next changes ~= 0 ] whileTrue.
- (format = #line) ifTrue: [ stdout nl ].
- ^space liveCells size
- ]
- " Cover method for generator that runs automaton a set number of turns "
- runTurns: numTurns [
- | gen |
- gen := self getTurnGenerator.
- " Calling runOneTurn with false for the last turn saves a lot of time "
- (numTurns - 1) timesRepeat: [ gen next ].
- self runOneTurn: false.
- (format = #line) ifTrue: [ stdout nl ].
- ^space liveCells size
- ]
- " Access "
- space [ ^space ]
- ]
Advertisement
Add Comment
Please, Sign In to add comment