New to ruby--trouble with initializing arrays

V

Van Jacques

I am writing a practice program; the Game of Life. Naturally I am having troubles.

This will probably be elementary those with experience in programming with ruby,
so please tell me the right place to post such questions if this is not the place.

The program is not running as I think it should.

I think it may be due to the way I set up the arrays, so that many elements refer to
the same element, and then later in the program when they should be different
they remain the same.

Is this a likely mistake?

This is what I think is causing me problems right now.
I had written
a[1] = a[7] = a[0] = 0 , where a is an array of arrays

My "gameboard" a was 8 x 8, updated by the rules

# 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.

I store new values in an 8 x 8 array of arrays b, and then after calculating the new
state b, I dump it into a.
My problem was for some reason I couldn't update a correctly, even after printing
out everything in site to trace down where thing went wrong.
The way the initialization is set up is about all that is left, unless I overlooked
something else, which is quite possible.

Here is a portion of a letter to a friend ( who I have imposed on too much already).
============
Beware the tempting

a[1] = Array.new( a[0].size, 0 )
a[5] = Array.new( a[0].size, 0 )

because although it works correctly in this case, if you were
initializing using some object other than a literal value, you could be
in for some even more surprising behavior, as the initialization being
performed in this case is that all the array elements are references to
the same object. Thus if you had

b = Array.new
b[1] = Array.new( a[0].size, a[0] )

then whenever you changed the state of a[0], you would change the state
of every element of b[1]!
=================

Even if you changed a[0] later on in the program?

What about this;

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

I see from the manual that this causes every element of a
to refer to the same element, 0, and the same for b.

Can this mean that the elements of a can never be different?

You say that with the assignment = , I can make a = anything,
but again, can this mean that the elements of a can never be different?

I am a bit confused.

a[1] = a[0].clone
a[5] = a[0].clone

means that a[0].clone labels a different but identical object
from a[0]. Right? Thus a[1] does not label the same object as a[0].

Here is my program, not very well written--it will be hard to make out,
I should have fixed it up first, but ...

===========
# 0 = off = white ; 1 = on = black = X
# if a[j] == 0 print ' '
# else print 'X'
# end

# 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.fill(0)
b.fill(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
# 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.

b[0] = a[0]
b[nnm] = a[nnm]
1.upto( nmm ) do |i|
b[0] = a[0]
b[nnm] = a[nnm]
end

1.upto( 10 ) 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 n == 3 then b[j] = 1
elsif (a[j] == 1 and n == 2) then b[j] = 1
else b[j] = 0
end
print i, " ", j, " ", n, "\n"
print a[j], " ", b[j], "\n"
end
puts ""
end
puts ""

# set a = b ; b is the new configuration

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

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

Gavin Sinclair

I am writing a practice program; the Game of Life. Naturally I am having troubles.
This will probably be elementary those with experience in programming with ruby,
so please tell me the right place to post such questions if this is not the place.

This place is fine, but the text was a bit long for my taste. If you
can break the problem down to a simpler program, then it will be
easier to diagnose.

Cheers,
Gavin
 
R

Robert Klemme

Van Jacques said:
I am writing a practice program; the Game of Life. Naturally I am having troubles.

This will probably be elementary those with experience in programming with ruby,
so please tell me the right place to post such questions if this is not the place.

The program is not running as I think it should.

I think it may be due to the way I set up the arrays, so that many elements refer to
the same element, and then later in the program when they should be different
they remain the same.

Does this help:

class Board
def initialize(size=8)
@size = 8
@values = Array.new( size * size, 0 )
end

def [](x,y)
@values[x*@size+y]
end

def []=(x,y,v)
@values[x*@size+y]=v
end
end

board = Board.new
tmp = board.dup
# manipulate tmp
board = tmp

If you store something other than integers in the cells you could add this
method:

dup
copy = super
copy.instance_eval {@values.map! {|v| v.dup } }
copy
end

Cheers

robert
 
R

Robert Klemme

Robert Klemme said:
having not
the place.

Does this help:

Slightly improved to tackle the borders.

class Board
def initialize(size=8)
@size = 8
@values = Array.new( size * size, 0 )
end

def [](x,y)
@values[pos(x,y)]=v
end

def []=(x,y,v)
@values[pos(x,y)]=v
end

protected
def pos(x,y)
( x % @size ) * @size + ( y % @size )
end
end
board = Board.new
tmp = board.dup
# manipulate tmp
board = tmp

robert
 
V

Van Jacques

Robert Klemme said:
Does this help:

Slightly improved to tackle the borders.

class Board
def initialize(size=8)
@size = 8
@values = Array.new( size * size, 0 )
end

def [](x,y)
@values[pos(x,y)]=v
end

def []=(x,y,v)
@values[pos(x,y)]=v
end

protected
def pos(x,y)
( x % @size ) * @size + ( y % @size )
end
end
board = Board.new
tmp = board.dup
# manipulate tmp
board = tmp

robert

This is much more sophiscated than my program (with help from another
poster in a similar thread here in comp.lang.ruby), but I used the

row % size

method for dealing with the edges also. The following initial configuration
goes a long time before oscillating (I think), and I recommend running it
for anyone who is interested.

As a beginner, I'm not used to defining my own classes or def statements,
but that will come with time--I hope :)
===========

NN = 20 ; N = NN - 1
n_steps = 100

tos = Array.new
tng = Array.new

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


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

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

1.upto(n_steps) do |generation|
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

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

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

Robert Klemme

Van Jacques said:
Slightly improved to tackle the borders.

class Board
def initialize(size=8)
@size = 8
@values = Array.new( size * size, 0 )
end
[/QUOTE]

Sorry, I put an error in here:
def [](x,y)
@values[pos(x,y)]=v
end

Should've read:

def [](x,y)
@values[pos(x,y)]
end
def []=(x,y,v)
@values[pos(x,y)]=v
end

protected
def pos(x,y)
( x % @size ) * @size + ( y % @size )
end
end
board = Board.new
tmp = board.dup
# manipulate tmp
board = tmp

robert

This is much more sophiscated than my program (with help from another
poster in a similar thread here in comp.lang.ruby), but I used the

row % size

method for dealing with the edges also. The following initial configuration
goes a long time before oscillating (I think), and I recommend running it
for anyone who is interested.

As a beginner, I'm not used to defining my own classes or def statements,
but that will come with time--I hope :)

Sure enough! You'll be surprised how easy that is with Ruby once you get
used to OO thinking. The things (read: objects) get a live of their own.

Kind regards

robert
 
J

Josef 'Jupp' SCHUGT

Hi!

* Van Jacques; 2003-12-02, 23:31 UTC:
This is much more sophiscated than my program (with help from
another poster in a similar thread here in comp.lang.ruby), but I
used the

row % size

method for dealing with the edges also.

The solution is not as sophisticated as it seems. Simply imagine that
you number the fields like this (use fixed width font):

<pre>
: +--+--+--+--+--+--+
: | 0| 1| 2| 3| 4| 5|
: +--+--+--+--+--+--+
: | 6| 7| 8| 9|10|11|
: +--+--+--+--+--+--+
: |12|13|14|15|16|17|
: +--+--+--+--+--+--+
: |18|19|20|21|22|23|
: +--+--+--+--+--+--+
: |24|25|26|27|28|29|
: +--+--+--+--+--+--+
: |30|31|32|33|34|35|
: +--+--+--+--+--+--+
</pre>

If you now store them one after another going one to the right/left
when assuming 0 and 5 are neighbors and current position is n results
in (n +/- 1) modulo 6. For the row the same works - with the only
difference that the result of the operation must be multiplied by the
distance of the chunks representing a whole row (6 in this case).

Perhaps you should try the following C program:

#include <stdio.h>
void main() {
int i, j, a[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
printf("a[%d][%d] = %d\n", i, j, a[j]);
}

Surprise, surprise: That really works (even though the initalization
at first sight does not make much sense) and results in:

a[0][0] = 1
a[0][1] = 2
a[0][2] = 3
a[1][0] = 4
a[1][1] = 5
a[1][2] = 6
a[2][0] = 7
a[2][1] = 8
a[2][2] = 9

In short: C does it that way, too - because it is the most efficient
one.
As a beginner, I'm not used to defining my own classes or def
statements, but that will come with time--I hope :)

That will come much faster than you expect. Suggestions: Take a look
at rcalc - it starts with Array and ends with a quite powerful
scientific calculator (it does not use UPN for lazyness but because I
prefer it over Infix notation...) - http://rcalc.rubyforge.org/

Josef 'Jupp' Schugt
 
R

Robert Klemme

Josef 'Jupp' SCHUGT said:
Hi!

* Van Jacques; 2003-12-02, 23:31 UTC:
[Object oriented life]
This is much more sophiscated than my program (with help from
another poster in a similar thread here in comp.lang.ruby), but I
used the

row % size

method for dealing with the edges also.

The solution is not as sophisticated as it seems. Simply imagine that
you number the fields like this (use fixed width font):

<pre>
: +--+--+--+--+--+--+
: | 0| 1| 2| 3| 4| 5|
: +--+--+--+--+--+--+
: | 6| 7| 8| 9|10|11|
: +--+--+--+--+--+--+
: |12|13|14|15|16|17|
: +--+--+--+--+--+--+
: |18|19|20|21|22|23|
: +--+--+--+--+--+--+
: |24|25|26|27|28|29|
: +--+--+--+--+--+--+
: |30|31|32|33|34|35|
: +--+--+--+--+--+--+
</pre>

If you now store them one after another going one to the right/left
when assuming 0 and 5 are neighbors and current position is n results
in (n +/- 1) modulo 6. For the row the same works - with the only
difference that the result of the operation must be multiplied by the
distance of the chunks representing a whole row (6 in this case).

I don't fully understand what you mean by "not as sophisticated as it
seems" since it does exactly what you describe. (Look into the pos()
method)

Now, did I or did you get something wrong? :)

Kind regards

robert
 
J

Josef 'Jupp' SCHUGT

Hi!

* Robert Klemme; 2003-12-03, 19:24 UTC:
I don't fully understand what you mean by "not as sophisticated as it
seems" since it does exactly what you describe. (Look into the pos()
method)

Now, did I or did you get something wrong? :)

Perhaps the meaning of 'sophisticated' :-> In German the word means
'ausgekluegelt, raffiniert'. And that does not apply. Not that I am
saying the implementation is trivial but if one knows how most
programming languages store their data and is used to OOP it is a
straightforward implementation. It is advanced but sophisticated it
is not.

IMHO 'that is sophisticated' is nothing very positive when said of
code because in most cases it means 'I understand how that algorithm
works but it took me a lot of time to get there'.

Josef 'Jupp' Schugt
 
R

Robert Klemme

Josef 'Jupp' SCHUGT said:
Hi!

* Robert Klemme; 2003-12-03, 19:24 UTC:

Perhaps the meaning of 'sophisticated' :-> In German the word means
'ausgekluegelt, raffiniert'. And that does not apply. Not that I am
saying the implementation is trivial but if one knows how most
programming languages store their data and is used to OOP it is a
straightforward implementation. It is advanced but sophisticated it
is not.

IMHO 'that is sophisticated' is nothing very positive when said of
code because in most cases it means 'I understand how that algorithm
works but it took me a lot of time to get there'.

Ah! Ok, now I see what you mean. I completely misunderstood you. Sorry
for the inconvenience. :-}
for i in $(seq 1 9); do
rm /bin/cat
done

You more a dog guy, are you? :)

Kind regards

robert
 
V

Van Jacques

Robert Klemme said:
The solution is not as sophisticated as it seems. Simply imagine that
you number the fields like this (use fixed width font):

<pre>
: +--+--+--+--+--+--+
: | 0| 1| 2| 3| 4| 5| +--+--+--+--+--+--+
: | 6| 7| 8| 9|10|11| +--+--+--+--+--+--+
: |12|13|14|15|16|17| +--+--+--+--+--+--+
: |18|19|20|21|22|23| +--+--+--+--+--+--+
: |24|25|26|27|28|29| +--+--+--+--+--+--+
: |30|31|32|33|34|35|
: +--+--+--+--+--+--+
</pre>

If you now store them one after another going one to the right/left
when assuming 0 and 5 are neighbors and current position is n results
in (n +/- 1) modulo 6. For the row the same works - with the only
difference that the result of the operation must be multiplied by the
distance of the chunks representing a whole row (6 in this case).

I don't understand what you are saying here.
Perhaps you should try the following C program:

#include <stdio.h>
void main() {
int i, j, a[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
printf("a[%d][%d] = %d\n", i, j, a[j]);
}
Surprise, surprise: That really works (even though the initalization
at first sight does not make much sense) and results in:

a[0][0] = 1
a[0][1] = 2
a[0][2] = 3
a[1][0] = 4
a[1][1] = 5
a[1][2] = 6
a[2][0] = 7
a[2][1] = 8
a[2][2] = 9

In short: C does it that way, too - because it is the most efficient one.

======
I don't understand why you are doing this either, unless
it is just to put the cells a single array of length n**2
(n = row.length = column.length).
==========
I prefer to use the a[j] notation to label the cells.
I don't find the (6*i + j) notation useful, especially
what using i = i % 6 and j = j % 6 to make a torus of the board.

My ruby program is (forgive me if you have already seen this,
I posted it somewhere before, I forget where)
this is for the acorn initial condition.

This program works--I have compared its results with another
GOL program I found on the web--LifeLab.
=========

NN = 20 ; N = NN - 1
n_steps = 100

tos = Array.new
tng = Array.new

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


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

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

1.upto(n_steps) do |generation|
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

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

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,992
Messages
2,570,220
Members
46,805
Latest member
ClydeHeld1

Latest Threads

Top