# ruby game of life program

Discussion in 'Ruby' started by Van Jacques, Nov 29, 2003.

1. ### Van JacquesGuest

Finally its right. It seems so obvious now. I thought I could
take this or that shortcut without thinking it thru.
I must be more methodical.
I couldn't quit till I did it.

Now to do some more interesting patterns.

Here it is if you are interested.

===========
# Size of grid;

nn = 8
nnm = nn - 1
nmm = nnm - 1

# Create and Initialize Grid a

a = Array.new
b = Array.new
0.upto( nnm ) do |i|
a = Array.new( nn )
b = Array.new( nn )
a = [0,0,0,0,0,0,0,0]
b = [0,0,0,0,0,0,0,0]
end

# Initial state
a[3][2] = 1
a[3][3] = 1
a[3][4] = 1
a[3][5] = 1

0.upto( nnm ) do |i|
print a
puts ""
end
puts ""

# Apply rules to set a[j] = 0 or 1
# What to do at edges?
# If a cell is off and has 3 living neighbors (out of 8), it will become alive
# in the next generation.
# If a cell is on and has 2 or 3 living neighbors, it survives; otherwise, it
# dies in the next generation. Otherwise no change.

1.upto( 6 ) do |k|
1.upto( nmm ) do |i|
1.upto( nmm ) do |j|

# count n = number of black cells around [j]

n = a[i-1][j-1] + a[i-1][j] + a[i-1][j+1] + a[j-1] + a[j+1] + \
a[i+1][j-1] + a[i+1][j] + a[i+1][j+1]

if (a[j] == 0 and n == 3) then b[j] = 1
elsif (a[j] == 1 && (n != 2 and n != 3)) then b[j] = 0
elsif (a[j] == 0 and n != 3) then b[j] = 0
elsif (a[j] == 1 && (n == 2 or n == 3)) then b[j] = 1
end
end
end

0.upto( nnm ) do |i|
0.upto( nnm ) do |j|
a[j] = b[j]
end
print a
puts ""
end
puts ""

0.upto( nnm ) do |i|
0.upto( nnm ) do |j|
if (a[i][j] == 0) then print ' '
else print 'X'
end
end
puts ""
end
end

=====
Sylvan Jacques ; (Van)[/i]

Van Jacques, Nov 29, 2003

2. ### Josef 'Jupp' SCHUGTGuest

Hi!

* Van Jacques; 2003-11-29, 12:40 UTC:
> Finally its right. It seems so obvious now. I thought I could take
> this or that shortcut without thinking it thru.
> I must be more methodical.
> I couldn't quit till I did it.

Here's some suggestions. 'tos' stands for 'the old situations', 'tng'
for 'the next generation'. You could as well use 'last_generation'
and 'next_generation'.

#!/usr/bin/env ruby
N = 7

tos = Array.new
tng = Array.new

0.upto(N) { |row|
tos[row] = Array.new(N+1)
tng[row] = Array.new(N+1)
tos[row] = [0, 0, 0, 0, 0, 0, 0, 0]
tng[row] = [0, 0, 0, 0, 0, 0, 0, 0]
}

2.upto(5) { |column|
tos[3][column] = 1
}

0.upto(N) { |row|
print tos[row], "\n"
}
puts

1.upto(6) { |generation|
1.upto(N - 1) { |row|
1.upto(N - 1) { |column|
neighbors = 0
-1.upto(1) { |row_offset|
-1.upto(1) { |column_offset|
unless row_offset == 0 and column_offset == 0
neighbors += tos[row+row_offset][column+column_offset]
end
}
}
if tos[row][column] == 0
tng[row][column] = neighbors == 3 ? 1 : 0
else
tng[row][column] = (neighbors == 2 or neighbors == 3) ? 1 : 0
end
}
}

0.upto(N) { |row|
0.upto(N) { |column| tos[row][column] = tng[row][column] }
print tos[row], "\n"
}
puts

0.upto(N) { |row|
0.upto(N) { |column|
print (tos[row][column] == 0 ? ' ' : 'X')
}
puts
}
}
#done

Wow, that was my first game of life ever :->

Josef 'Jupp' Schugt
--
begin SPAM-POLICY.txt.vbs
if msg.size > 100 kB or msg.sender.is_spammer or msg.text.is_spam
end

Josef 'Jupp' SCHUGT, Nov 29, 2003

3. ### Van JacquesGuest

Thanks for the suggestions. A great improvement. I was thinking about what to do
about the edges when the initial pattern expanded to the edges.
(This initial pattern stays in the middle, and only changes for the first
few generations, but others do all kinds of things, as I'm sure most
people know.) Have you run this? I am going to.
You appear to know the language pretty well.

On the LHS (row = 0) the edges won't be a problem, since -1 in ruby
will wrap around to the RHS. But I haven't thought about what to
do when a live cell reaches the RHS, since when row goes
out of range it will get nil. Maybe I can turn nil into 0.

I will have the same behavior at the top and bottom of the grid too.

=============

"Josef 'Jupp' SCHUGT" <> wrote in message news:<20031129174431.GB2320@jupp%gmx.de>...
>
> Here's some suggestions. 'tos' stands for 'the old situations', 'tng'
> for 'the next generation'. You could as well use 'last_generation'
> and 'next_generation'.
>
> #!/usr/bin/env ruby
> N = 7
>
> tos = Array.new
> tng = Array.new
>
> 0.upto(N) { |row|
> tos[row] = Array.new(N+1)
> tng[row] = Array.new(N+1)
> tos[row] = [0, 0, 0, 0, 0, 0, 0, 0]
> tng[row] = [0, 0, 0, 0, 0, 0, 0, 0]
> }
>
> 2.upto(5) { |column|
> tos[3][column] = 1
> }
>
> 0.upto(N) { |row|
> print tos[row], "\n"
> }
> puts
>
> 1.upto(6) { |generation|
> 1.upto(N - 1) { |row|
> 1.upto(N - 1) { |column|
> neighbors = 0
> -1.upto(1) { |row_offset|
> -1.upto(1) { |column_offset|
> unless row_offset == 0 and column_offset == 0
> neighbors += tos[row+row_offset][column+column_offset]
> end
> }
> }
> if tos[row][column] == 0
> tng[row][column] = neighbors == 3 ? 1 : 0
> else
> tng[row][column] = (neighbors == 2 or neighbors == 3) ? 1 : 0
> end
> }
> }
>
> 0.upto(N) { |row|
> 0.upto(N) { |column| tos[row][column] = tng[row][column] }
> print tos[row], "\n"
> }
> puts
>
> 0.upto(N) { |row|
> 0.upto(N) { |column|
> print (tos[row][column] == 0 ? ' ' : 'X')
> }
> puts
> }
> }
> #done
>
> Wow, that was my first game of life ever :->
>
> Josef 'Jupp' Schugt

Van Jacques, Dec 1, 2003
4. ### Van JacquesGuest

"Josef 'Jupp' SCHUGT" <> wrote in message news:<20031129174431.GB2320@jupp%gmx.de>...
> #!/usr/bin/env ruby
> N = 7
>
> tos = Array.new
> tng = Array.new
>
> 0.upto(N) { |row|
> tos[row] = Array.new(N+1)
> tng[row] = Array.new(N+1)
> tos[row] = [0, 0, 0, 0, 0, 0, 0, 0]
> tng[row] = [0, 0, 0, 0, 0, 0, 0, 0]
> }
> ...

> Wow, that was my first game of life ever :->
>
> Josef 'Jupp' Schugt

One thing I notice was that I used

a = Array.new
b = Array.new
0.upto( size - 1 ) do |i|
a = Array.new(size, 0)
b = Array.new(size, 0)
end

while you used

tos = Array.new
tng = Array.new

0.upto(N) { |row|
tos[row] = Array.new(N+1)
tng[row] = Array.new(N+1)
tos[row] = [0, 0, 0, 0, 0, 0, 0, 0]
tng[row] = [0, 0, 0, 0, 0, 0, 0, 0] }

==========

Is there any difference? It seems easier to use

tos[row] = Array.new(N+1, 0)

rather than

tos[row] = [0, 0, 0, 0, 0, 0, 0, 0] .

Van

Van Jacques, Dec 1, 2003
5. ### Josef 'Jupp' SCHUGTGuest

Hi!

* Van Jacques; 2003-12-01, 12:52 UTC:
> "Josef 'Jupp' SCHUGT" <> wrote in message news:<20031129174431.GB2320@jupp%gmx.de>...
> One thing I notice was that I used
>
> a = Array.new
> b = Array.new
> 0.upto( size - 1 ) do |i|
> a = Array.new(size, 0)
> b = Array.new(size, 0)
> end
>
> while you used
>
> tos = Array.new
> tng = Array.new
>
> 0.upto(N) { |row|
> tos[row] = Array.new(N+1)
> tng[row] = Array.new(N+1)
> tos[row] = [0, 0, 0, 0, 0, 0, 0, 0]
> tng[row] = [0, 0, 0, 0, 0, 0, 0, 0] }

You seem to be confusing something. In the message I did answer to
you did use

a = Array.new
b = Array.new
0.upto( nnm ) do |i|
a = Array.new( nn )
b = Array.new( nn )
a = [0,0,0,0,0,0,0,0]
b = [0,0,0,0,0,0,0,0]
end

I was assuming you did intentionally implement it in that way (to
better point out the initial values) and so I saw no reson to change
it.

Josef 'Jupp' Schugt
--
for i in \$(seq 1 9); do
rm /bin/cat
done

Josef 'Jupp' SCHUGT, Dec 1, 2003
6. ### Van JacquesGuest

(Van Jacques) wrote in message news:<>...
> "Josef 'Jupp' SCHUGT" <> wrote in message news:<20031129174431.GB2320@jupp%gmx.de>...

I decided to handle the edges by making the grid into a torus, using
mod NN,
where I set NN = grid edge, N = NN - 1, in Josef's notation. Then the
part of the
program that decides that next generation becomes

0.upto(N) do |row|
0.upto(N) do |column|
neighbors = 0
-1.upto(1) do |row_offset|
-1.upto(1) do |column_offset|
unless row_offset == 0 and column_offset == 0
i = (row+row_offset) % NN
j = (column+column_offset) % NN
neighbors += tos[j]
end
end
end
if tos[row][column] == 0
tng[row][column] = (neighbors == 3) ? 1 : 0
else
tng[row][column] = (neighbors == 2 or neighbors == 3) ? 1 : 0
end
end
end

============

Since mod NN connects top and bottom, and LHS with RHS, we can do all
the rows and columns
from 0 to N = NN - 1.

I also used a new pattern and more steps or generations.

With NN = 11 = # of rows and cols., I used the initial condition know
as an "acorn";

tos[4][4] = tos[5][6] = tos[6][3] = tos[6][4] = 1
tos[6][7] = tos[6][8] = tos[6][9] = 1

and increased to # of steps to 40. The pattern is much more
interesting, and becomes
stable at step 34.

If one increases NN to 20, and # of steps to several hundred, the
behavior is far more
complex. The variety of behaviors of even simple games of life is
amazing.

I recommend this as a practice program for anyone learning to program.

Van Jacques, Dec 1, 2003
7. ### Van JacquesGuest

"Josef 'Jupp' SCHUGT" <> wrote in message news:<20031201153328.GF605@jupp%gmx.de>...
> Hi!
>
> * Van Jacques; 2003-12-01, 12:52 UTC:
> > "Josef 'Jupp' SCHUGT" <> wrote in message news:<20031129174431.GB2320@jupp%gmx.de>...
> > One thing I notice was that I used

.....
>
> You seem to be confusing something. In the message I did answer to
> you did use
>
> a = Array.new
> b = Array.new
> 0.upto( nnm ) do |i|
> a = Array.new( nn )
> b = Array.new( nn )
> a = [0,0,0,0,0,0,0,0]
> b = [0,0,0,0,0,0,0,0]
> end
>
> I was assuming you did intentionally implement it in that way (to
> better point out the initial values) and so I saw no reson to change
> it.
>
> Josef 'Jupp' Schugt

I see. I must have changed it after I made the first post.

Van Jacques, Dec 2, 2003