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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
////////////////////////////////////////////////////////////////////////
// COMP1521 20T2 --- assignment 1: a cellular automaton renderer
//
// This code runs a one-dimensional, three-neighbour cellular automaton.
// It examines its neighbours and its value in the previous generation
// to derive the value for the next generation.
//
// . . . . # . . . .
// . . . # # # . . .
// . . # # . . # . .
// . # # . # # # # .
// # # . . # . . . #
//
// Here, we using '#' to indicate a cell that's alive; and '.' to
// indicate a cell that is not.
//
// Given we examine three neighbours, there are eight states that the
// prior cells could be in. They are:
//
// . . . . . # . # . . # # # . . # . # # # . # # #
// 0 1 2 3 4 5 6 7
//
// For each one, we decide what action to take. For example, we might
// choose to have the following `rule':
//
// . . . . . # . # . . # # # . . # . # # # . # # #
// . # # # # . . .
//
// We apply this rule to every cell, to determine whether the next state
// is alive or dead; and this forms the next generation. If we print
// these generations, one after the other, we can get some interesting
// patterns.
//
// The description of the rule above --- by example, showing each case
// and how it should be handled --- is inefficient. We can abbreviate
// this rule by reading it in binary, considering live cells as 1's and
// dead cells as 0s; and if we consider the prior states to be a binary
// value too --- the above rule could be 0b00001110, or 30.
//
// To use that rule, we would mix together the previous states we're
// interested in --- left, middle, and right --- which tells us which
// bit of the rule value gives our next state.
//
// The world size, rule and the number of generations, are read from stdin:
//
// $ ./cellular
// Enter world size: 60
// Enter rule: 30
// Enter how many generations: 10
//
// 0 ..............................#.............................
// 1 .............................###............................
// 2 ............................##..#...........................
// 3 ...........................##.####..........................
// 4 ..........................##..#...#.........................
// 5 .........................##.####.###........................
// 6 ........................##..#....#..#.......................
// 7 .......................##.####..######......................
// 8 ......................##..#...###.....#.....................
// 9 .....................##.####.##..#...###....................
// 10 ....................##..#....#.####.##..#...................
//
// $ ./cellular
// Enter world size: 60
// Enter rule: 30
// Enter how many generations: -10
//
// 10 ....................##..#....#.####.##..#...................
// 9 .....................##.####.##..#...###....................
// 8 ......................##..#...###.....#.....................
// 7 .......................##.####..######......................
// 6 ........................##..#....#..#.......................
// 5 .........................##.####.###........................
// 4 ..........................##..#...#.........................
// 3 ...........................##.####..........................
// 2 ............................##..#...........................
// 1 .............................###............................
// 0 ..............................#.............................
#include <stdio.h>
#include <stdint.h>
// maximum and minimum values for the 3 parameters
#define MIN_WORLD_SIZE 1
#define MAX_WORLD_SIZE 128
#define MIN_GENERATIONS -256
#define MAX_GENERATIONS 256
#define MIN_RULE 0
#define MAX_RULE 255
// characters used to print alive/dead cells
#define ALIVE_CHAR '#'
#define DEAD_CHAR '.'
// `cells' is used to store successive generations. Each byte will be 1
// if the cell is alive in that generation, and 0 otherwise.
static int8_t cells[MAX_GENERATIONS + 1][MAX_WORLD_SIZE];
static void run_generation(int world_size, int which_generation, int rule);
static void print_generation(int world_size, int which_generation);
int main(int argc, char *argv[]) {
// read 3 integer parameters from stdin
printf("Enter world size: ");
int world_size = 0;
scanf("%d", &world_size);
if (world_size < MIN_WORLD_SIZE || world_size > MAX_WORLD_SIZE) {
printf("Invalid world size\n");
return 1;
}
printf("Enter rule: ");
int rule = 0;
scanf("%d", &rule);
if (rule < MIN_RULE || rule > MAX_RULE) {
printf("Invalid rule\n");
return 1;
}
printf("Enter how many generations: ");
int n_generations = 0;
scanf("%d", &n_generations);
if (n_generations < MIN_GENERATIONS || n_generations > MAX_GENERATIONS) {
printf("Invalid number of generations\n");
return 1;
}
putchar('\n');
// negative generations means show the generations in reverse
int reverse = 0;
if (n_generations < 0) {
reverse = 1;
n_generations = -n_generations;
}
// the first generation always has a only single cell which is alive
// this cell is in the middle of the world
cells[0][world_size / 2] = 1;
for (int g = 1; g <= n_generations; g++) {
run_generation(world_size, g, rule);
}
if (reverse) {
for (int g = n_generations; g >= 0; g--) {
print_generation(world_size, g);
}
} else {
for (int g = 0; g <= n_generations; g++) {
print_generation(world_size, g);
}
}
return 0;
}
//
// Calculate a new generation using rule and store it in cells
//
static void run_generation(int world_size, int which_generation, int rule) {
for (int x = 0; x < world_size; x++) {
// Get the values in the left and right neighbour cells.
// This requires some care, otherwise we could read beyond the
// bounds of the array. In the cases we are at the limits of
// the function, we consider those out-of-bounds cells zero.
int left = 0;
if (x > 0) {
left = cells[which_generation - 1][x - 1];
}
int centre = cells[which_generation - 1][x];
int right = 0;
if (x < world_size - 1) {
right = cells[which_generation - 1][x + 1];
}
// Convert the left, centre, and right states into one value.
int state = left << 2 | centre << 1 | right << 0;
// And check whether that bit is set or not in the rule.
// by testing the corresponding bit of the rule number.
int bit = 1 << state;
int set = rule & bit;
if (set) {
cells[which_generation][x] = 1;
} else {
cells[which_generation][x] = 0;
}
}
}
//
// Print out the specified generation
//
static void print_generation(int world_size, int which_generation) {
printf("%d", which_generation);
putchar('\t');
for (int x = 0; x < world_size; x++) {
if (cells[which_generation][x]) {
putchar(ALIVE_CHAR);
} else {
putchar(DEAD_CHAR);
}
}
putchar('\n');
}
|