Life in one dimension

From Rosetta Code

Jump to: navigation, search

Programming Task
This is a programming task. It lays out a problem which Rosetta Code users are encouraged to solve, using languages they know.

Code examples should be formatted along the lines of one of the existing prototypes.

One dimensional cellular automata

Assume an array of cells with an initial distribution of live and dead cells, and imaginary cells off the end of the array having fixed values.

Cells in the next generation of the array are calculated based on the value of the cell and its left and right nearest neighbours in the current generation. If, in the following table, a live cell is represented by 1 and a dead cell by 0 then to generate the value of the cell at a particular index in the array of cellular values you use the following table:

000 -> 0  # 
001 -> 0  #
010 -> 0  # Dies without enough neighbours
011 -> 1  # Needs one neighbour to survive
100 -> 0  #
101 -> 1  # Two neighbours giving birth
110 -> 1  # Needs one neighbour to survive
111 -> 0  # Starved to death.

Contents

[edit] Ada

 
with Ada.Text_IO;  use Ada.Text_IO;
 
procedure Cellular_Automata is
   type Petri_Dish is array (Positive range <>) of Boolean;
 
   procedure Step (Culture : in out Petri_Dish) is
      Left  : Boolean := False;
      This  : Boolean;
      Right : Boolean;
   begin
      for Index in Culture'First..Culture'Last - 1 loop
         Right := Culture (Index + 1);
         This  := Culture (Index);
         Culture (Index) := (This and (Left xor Right)) or (not This and Left and Right);
         Left := This;
      end loop;
      Culture (Culture'Last) := Culture (Culture'Last) and not Left;
   end Step;
 
   procedure Put (Culture : Petri_Dish) is
   begin
      for Index in Culture'Range loop
         if Culture (Index) then
            Put ('#');
         else
            Put ('_');
         end if;
      end loop;
   end Put;
 
   Culture : Petri_Dish :=
      (  False, True, True,  True, False, True,  True, False, True, False, True,
         False, True, False, True, False, False, True, False, False
      );
begin
   for Generation in 0..9 loop
      Put ("Generation" & Integer'Image (Generation) & ' ');
      Put (Culture);
      New_Line;
      Step (Culture);
   end loop;
end Cellular_Automata;
 

The implementation defines Petri dish type with Boolean items identifying whether a place is occupied by a living cell. State transition is determined by a simple Boolean expression of three arguments. Sample output:

Generation 0 _###_##_#_#_#_#__#__
Generation 1 _#_#####_#_#_#______
Generation 2 __##___##_#_#_______
Generation 3 __##___###_#________
Generation 4 __##___#_##_________
Generation 5 __##____###_________
Generation 6 __##____#_#_________
Generation 7 __##_____#__________
Generation 8 __##________________
Generation 9 __##________________

[edit] ALGOL 68

[edit] Using the low level packed arrays of BITS manipulation operators

INT stop generation = 9;
INT universe width = 20;
FORMAT alive or dead = $b("#","_")$;
 
BITS universe := 2r01110110101010100100;
   # universe := BIN ( ENTIER ( random * max int ) ); #
INT upb universe = bits width;
INT lwb universe = bits width - universe width + 1;
 
PROC couple = (BITS parent, INT lwb, upb)BOOL: (
  SHORT INT sum := 0;
  FOR bit FROM lwb TO upb DO
    sum +:= ABS (bit ELEM parent)
  OD;
  sum = 2
);

FOR generation FROM 0 WHILE
  printf(($"Generation "d": "$, generation,
         $f(alive or dead)$, []BOOL(universe)[lwb universe:upb universe],
         $l$));
# WHILE # generation < stop generation DO
  BITS next universe := 2r0;  
  
  # process the first event horizon manually #
  IF couple(universe,lwb universe,lwb universe + 1) THEN 
    next universe := 2r10
  FI;
  
  # process the middle kingdom in a loop #
  FOR bit FROM lwb universe + 1 TO upb universe - 1 DO 
    IF couple(universe,bit-1,bit+1) THEN
      next universe := next universe OR 2r1
    FI;
    next universe := next universe SHL 1
  OD; 

  # process the last event horizon manually #
  IF couple(universe, upb universe - 1, upb universe) THEN 
    next universe := next universe OR 2r1
  FI;
  universe := next universe
OD

[edit] Using high level BOOL arrays

INT stop generation = 9;
INT upb universe = 20;
FORMAT alive or dead = $b("#","_")$;
 
BITS bits universe := 2r01110110101010100100;
   # bits universe := BIN ( ENTIER ( random * max int ) ); #
[upb universe] BOOL universe := []BOOL(bits universe)[bits width - upb universe + 1:];
 
PROC couple = (REF[]BOOL parent)BOOL: (
  SHORT INT sum := 0;
  FOR bit FROM LWB parent TO UPB parent DO
    sum +:= ABS (parent[bit])
  OD;
  sum = 2
);

FOR generation FROM 0 WHILE
  printf(($"Generation "d": "$, generation,
         $f(alive or dead)$, universe,
         $l$));
# WHILE # generation < stop generation DO
  [UPB universe]BOOL next universe;
  
  # process the first event horizon manually #
  next universe[1] := couple(universe[:2]);
  
  # process the middle kingdom in a loop #
  FOR bit FROM LWB universe + 1 TO UPB universe - 1 DO 
    next universe[bit] := couple(universe[bit-1:bit+1])
  OD; 

  # process the last event horizon manually #
  next universe[UPB universe] := couple(universe[UPB universe - 1: ]);
  universe := next universe
OD

Output:

Generation 0: _###_##_#_#_#_#__#__
Generation 1: _#_#####_#_#_#______
Generation 2: __##___##_#_#_______
Generation 3: __##___###_#________
Generation 4: __##___#_##_________
Generation 5: __##____###_________
Generation 6: __##____#_#_________
Generation 7: __##_____#__________
Generation 8: __##________________
Generation 9: __##________________

[edit] BASIC

Works with: QuickBasic version 4.5

Translation of: Java

DECLARE FUNCTION life$ (lastGen$)
DECLARE FUNCTION getNeighbors! (group$)
CLS
start$ = "_###_##_#_#_#_#__#__"
numGens = 10
FOR i = 0 TO numGens - 1
	PRINT "Generation"; i; ": "; start$
	start$ = life$(start$)
NEXT i
 
FUNCTION getNeighbors (group$)
		ans = 0
		IF (MID$(group$, 1, 1) = "#") THEN ans = ans + 1
		IF (MID$(group$, 3, 1) = "#") THEN ans = ans + 1
		getNeighbors = ans
END FUNCTION
 
FUNCTION life$ (lastGen$)
		newGen$ = ""
		FOR i = 1 TO LEN(lastGen$)
			neighbors = 0
			IF (i = 1) THEN 'left edge
				IF MID$(lastGen$, 2, 1) = "#" THEN
					neighbors = 1
				ELSE
					neighbors = 0
				END IF
			ELSEIF (i = LEN(lastGen$)) THEN 'right edge
				IF MID$(lastGen$, LEN(lastGen$) - 1, 1) = "#" THEN
					neighbors = 1
				ELSE
					neighbors = 0
				END IF
			ELSE 'middle
				neighbors = getNeighbors(MID$(lastGen$, i - 1, 3))
			END IF
 
			IF (neighbors = 0) THEN 'dies or stays dead with no neighbors
				newGen$ = newGen$ + "_"
			END IF
			IF (neighbors = 1) THEN 'stays with one neighbor
				newGen$ = newGen$ + MID$(lastGen$, i, 1)
			END IF
			IF (neighbors = 2) THEN 'flips with two neighbors
				IF MID$(lastGen$, i, 1) = "#" THEN
					newGen$ = newGen$ + "_"
				ELSE
					newGen$ = newGen$ + "#"
				END IF
			END IF
		NEXT i
		life$ = newGen$
END FUNCTION

Output:

Generation 0 : _###_##_#_#_#_#__#__
Generation 1 : _#_#####_#_#_#______
Generation 2 : __##___##_#_#_______
Generation 3 : __##___###_#________
Generation 4 : __##___#_##_________
Generation 5 : __##____###_________
Generation 6 : __##____#_#_________
Generation 7 : __##_____#__________
Generation 8 : __##________________
Generation 9 : __##________________

[edit] Fortran

Works with: Fortran version 90 and later

PROGRAM LIFE_1D
   
  IMPLICIT NONE

  LOGICAL :: cells(20) = (/ .FALSE., .TRUE., .TRUE., .TRUE., .FALSE., .TRUE., .TRUE., .FALSE., .TRUE., .FALSE., &
                            .TRUE., .FALSE., .TRUE., .FALSE., .TRUE., .FALSE., .FALSE., .TRUE., .FALSE., .FALSE. /)
  INTEGER :: i
  
  DO i = 0, 9
     WRITE(*, "(A,I0,A)", ADVANCE = "NO") "Generation ", i, ": "
     CALL Drawgen(cells)
     CALL Nextgen(cells)
  END DO

CONTAINS

  SUBROUTINE Nextgen(cells)
    LOGICAL, INTENT (IN OUT) :: cells(:)
    LOGICAL :: left, centre, right
    INTEGER :: i
   
    left = .FALSE.
    DO i = 1, SIZE(cells)-1
       centre = cells(i)
       right = cells(i+1)
       IF (left .AND. right) THEN
          cells(i) = .NOT. cells(i)
       ELSE IF (.NOT. left .AND. .NOT. right) THEN
          cells(i) = .FALSE.
       END IF
       left = centre
    END DO
    cells(SIZE(cells)) = left .AND. right
  END SUBROUTINE Nextgen

  SUBROUTINE Drawgen(cells)
    LOGICAL, INTENT (IN OUT) :: cells(:)
    INTEGER :: i
   
    DO i = 1, SIZE(cells)
       IF (cells(i)) THEN
          WRITE(*, "(A)", ADVANCE = "NO") "#"
       ELSE
          WRITE(*, "(A)", ADVANCE = "NO") "_"
       END IF
    END DO
    WRITE(*,*)
  END SUBROUTINE Drawgen
  
END PROGRAM LIFE_1D

Output

Generation 0: _###_##_#_#_#_#__#__ 
Generation 1: _#_#####_#_#_#______ 
Generation 2: __##___##_#_#_______ 
Generation 3: __##___###_#________ 
Generation 4: __##___#_##_________ 
Generation 5: __##____###_________ 
Generation 6: __##____#_#_________ 
Generation 7: __##_____#__________ 
Generation 8: __##________________ 
Generation 9: __##________________ 

[edit] Haskell

module Life1D where

import Data.List
import System.Random
import Control.Monad
import Control.Arrow

bnd :: [Char] -> Char
bnd bs =
   case bs of
        "_##" -> '#'
        "#_#" -> '#'
        "##_" -> '#'
        _     -> '_'

donxt xs = unfoldr(\xs -> case xs of [_,_] -> Nothing ;
                                      _ -> Just (bnd $ take 3 xs, drop 1 xs))  $ '_':xs++"_"

lahmahgaan xs = init.until (liftM2 (==) last (last. init)) (ap (++)(return. donxt. last)) $ [xs, donxt xs]

main = do
   g <- newStdGen
   let oersoep = map ("_#"!!). take 36 $ randomRs(0,1) g 
   mapM_ print . lahmahgaan $ oersoep

Some output:

*Life1D> mapM_ print . lahmahgaan $ "_###_##_#_#_#_#__#__"
"_###_##_#_#_#_#__#__"
"_#_#####_#_#_#______"
"__##___##_#_#_______"
"__##___###_#________"
"__##___#_##_________"
"__##____###_________"
"__##____#_#_________"
"__##_____#__________"
"__##________________"
 
*Life1D> main
"__##_##__#____###__#__#_______#_#_##"
"__#####_______#_#______________#_###"
"__#___#________#________________##_#"
"________________________________###_"
"________________________________#_#_"
"_________________________________#__"
"____________________________________"

[edit] J

life1d=: '_#'{~]@(([:3&(2=+/\)0,],0:)^:a:)

Example use:

   life1d ? 20 # 2
_###_##_#_#_#_#__#__
_#_#####_#_#_#______
__##___##_#_#_______
__##___###_#________
__##___#_##_________
__##____###_________
__##____#_#_________
__##_____#__________
__##________________

[edit] Java

This example requires a starting generation of at least length two (which is what you need for anything interesting anyway).

public class Life{
	public static void main(String[] args) throws Exception{
		String start= "_###_##_#_#_#_#__#__";
		int numGens = 10;
		for(int i= 0; i < numGens; i++){
			System.out.println("Generation " + i + ": " + start);
			start= life(start);
		}
	}
 
	public static String life(String lastGen){
		String newGen= "";
		for(int i= 0; i < lastGen.length(); i++){
			int neighbors= 0;
			if (i == 0){//left edge
				neighbors= lastGen.charAt(1) == '#' ? 1 : 0;
			} else if (i == lastGen.length() - 1){//right edge
				neighbors= lastGen.charAt(lastGen.length() - 2) == '#' ? 1 : 0;
			} else{//middle
				neighbors= getNeighbors(lastGen.substring(i - 1, i + 2));
			}
 
			if (neighbors == 0){//dies or stays dead with no neighbors
				newGen+= "_";
			}
			if (neighbors == 1){//stays with one neighbor
				newGen+= lastGen.charAt(i);
			}
			if (neighbors == 2){//flips with two neighbors
				newGen+= lastGen.charAt(i) == '#' ? "_" : "#";
			}
		}
		return newGen;
	}
 
	public static int getNeighbors(String group){
		int ans= 0;
		if (group.charAt(0) == '#') ans++;
		if (group.charAt(2) == '#') ans++;
		return ans;
	}
}

Output:

Generation 0: _###_##_#_#_#_#__#__
Generation 1: _#_#####_#_#_#______
Generation 2: __##___##_#_#_______
Generation 3: __##___###_#________
Generation 4: __##___#_##_________
Generation 5: __##____###_________
Generation 6: __##____#_#_________
Generation 7: __##_____#__________
Generation 8: __##________________
Generation 9: __##________________

[edit] OCaml

let get g i =
  try g.(i)
  with _ -> 0
 
let next_cell g i =
  match get g (i-1), get g (i), get g (i+1) with
  | 0, 0, 0 -> 0
  | 0, 0, 1 -> 0
  | 0, 1, 0 -> 0
  | 0, 1, 1 -> 1
  | 1, 0, 0 -> 0
  | 1, 0, 1 -> 1
  | 1, 1, 0 -> 1
  | 1, 1, 1 -> 0
  | _ -> assert(false)
 
let next g =
  let old_g = Array.copy g in
  for i = 0 to pred(Array.length g) do
    g.(i) <- (next_cell old_g i)
  done
 
let print_g g =
  for i = 0 to pred(Array.length g) do
    if g.(i) = 0
    then print_char '_'
    else print_char '#'
  done;
  print_newline()
 

put the code above in a file named "life.ml", and then use it in the ocaml toplevel like this:

#use "life.ml" ;;

let iter n g =
  for i = 0 to n do
    Printf.printf "Generation %d: " i; print_g g;
    next g;
  done
;;

let g_of_string str =
  let f = (function '_' -> 0 | '#' -> 1 | _ -> assert false) in
  Array.init (String.length str) (fun i -> f str.[i])
;;

# iter 9 (g_of_string "_###_##_#_#_#_#__#__") ;;
Generation 0: _###_##_#_#_#_#__#__
Generation 1: _#_#####_#_#_#______
Generation 2: __##___##_#_#_______
Generation 3: __##___###_#________
Generation 4: __##___#_##_________
Generation 5: __##____###_________
Generation 6: __##____#_#_________
Generation 7: __##_____#__________
Generation 8: __##________________
Generation 9: __##________________
- : unit = ()

[edit] Python

import random
 
printdead, printlive = '_#'
maxgenerations = 10
cellcount = 20
offendvalue = '0'
 
universe = ''.join(random.choice('01') for i in range(cellcount))
 
neighbours2newstate = {
 '000': '0',
 '001': '0',
 '010': '0',
 '011': '1',
 '100': '0',
 '101': '1',
 '110': '1',
 '111': '0',
 }
 
for i in range(maxgenerations):
    print "Generation %3i:  %s" % ( i,
          universe.replace('0', printdead).replace('1', printlive) )
    universe = offendvalue + universe + offendvalue
    universe = ''.join(neighbours2newstate[universe[i:i+3]] for i in range(cellcount))
 

Sample output:

Generation   0:  _###_##_#_#_#_#__#__
Generation   1:  _#_#####_#_#_#______
Generation   2:  __##___##_#_#_______
Generation   3:  __##___###_#________
Generation   4:  __##___#_##_________
Generation   5:  __##____###_________
Generation   6:  __##____#_#_________
Generation   7:  __##_____#__________
Generation   8:  __##________________
Generation   9:  __##________________
The following implementation uses boolean operations to realize the function.
import random
 
nquads = 5
maxgenerations = 10
fmt = '%%0%ix'%nquads
nbits = 4*nquads
a = random.getrandbits(nbits)  << 1
#a = int('01110110101010100100', 2) << 1
endmask = (2<<nbits)-2;
endvals = 0<<(nbits+1) | 0
tr = ('____', '___#', '__#_', '__##', '_#__', '_#_#', '_##_', '_###',
      '#___', '#__#', '#_#_', '#_##', '##__', '##_#', '###_', '####' )
for i in range(maxgenerations):
   print "Generation %3i:  %s" % (i,(''.join(tr[int(t,16)] for t in (fmt%(a>>1)))))
   a |= endvals
   a = ((a&((a<<1) | (a>>1))) ^ ((a<<1)&(a>>1))) & endmask

[edit] Vedit macro language

This implementation writes the calculated patterns into an edit buffer, where the results can viewed and saved into a file if required. The edit buffer also acts as storage during calculations.

IT("Gen 0: ..###.##.#.#.#.#..#.....")     // initial pattern
#9  = Cur_Col

for (#8 = 1; #8 < 10; #8++) {             // 10 generations
    Goto_Col(7)
    Reg_Empty(20)
    while (Cur_Col < #9-1) {
        if (Match("|{##|!#,#.#,|!###}")==0) {
            Reg_Set(20, "#", APPEND)
        } else {
            Reg_Set(20, ".", APPEND)
        }
        Char
    }
    EOL IN
    IT("Gen ") Num_Ins(#8, LEFT+NOCR) IT(": ")
    Reg_Ins(20)
}

Sample output:

Gen 0: ..###.##.#.#.#.#..#.....
Gen 1: ..#.#####.#.#.#.........
Gen 2: ...##...##.#.#..........
Gen 3: ...##...###.#...........
Gen 4: ...##...#.##............
Gen 5: ...##....###............
Gen 6: ...##....#.#............
Gen 7: ...##.....#.............
Gen 8: ...##...................
Gen 9: ...##...................
Personal tools