Question
Please I need help with this project, it needs to be written in a C programming language. Task Cellular automata have a long history in
Please I need help with this project, it needs to be written in a C programming language.
Task
Cellular automata have a long history in mathematics and computer science and were first discovered in the 1940s by John von Neumann and Stanislaw Ulam, two of the founding fathers of computer science. Since then they have found many applications in diverse field including artificial life, physics and biology. While simple to describe and implement, they are capable of generating amazingly complex behaviour, including chaotic dynamics and universal computation.
For this task you will implement the simplest type of cellular automaton - an elementary cellular automaton. You should first read this chapter, which will give you a good introduction to what an elementary cellular automaton is (you only need to read up to and not including Section 7.6). It also provides some example code. You should not copy this code directly, but use it to help you understand how to implement your own program.
Your program when run should take three values from the user as command line arguments. These values are:
- the number of cells in the one-dimensional grid (a positive integer)
- the number of generations to evolve and output the cellular automata for (a positive integer)
- the Wolfram code specifying which the rule set to evolve your cellular automata with (an integer in the range [0,255]).
These three arguments should be specified in this order on the command line when the user runs your program. You should check that the arguments are all valid and within the ranges given. If the user enters the wrong number of arguments or invalid arguments e.g. the number of cells as -10, then your program should print an error message and exit with a non-zero exit value.
For the values specified by the user, your program should calculate and print each generation of the cellular automata on a newline. You should print a * (asterisk) character to show a cell is alive, and a (space) character to show a cell is dead. For the initial generation, you should start with a row of cells that are all dead, except for the centre cell (or left of centre cell for an even number of cells) which is alive. You should assume that your row wraps around, that is, the neighbour of the left-most cell is the right-most cell and vice versa.
For example, the output when the users run your program with 30 cells for 20 generations with rule 110 should be:
$ ./task1 30 20 110 * ** *** ** * ***** ** * *** ** ** * *** ******* * ** *** *** ** * ** * ***** ***** ** * ** * *** ** *** **** * *** ** * ** ***** * ******* ** *** * **** ** * ** ** * ***** *** *** **** * ** *
You are free to choose whichever method you want to implement your program with the following restrictions:
- your implementation must contain a correctly declared main function
- you are not allowed to use global variables
Some hints for possible ways to implement your program are given below.
You could store the states of cells in the current and next generations in two arrays. The length of each array will be the number of cells specified by the user. Since this length is specified at runtime, you will need to allocate the memory for these arrays dynamically using malloc or calloc.
The main computation in your program could be performed by two nested loops. The outer loop would iterate over each generation. For each generation, an inner loop would iterate over all the cells in the current generation array. Based on the state in the current generation of each cell and its two neighbours, you can calculate the state in the next generation and store this in the next generation array. After calculating the next generation states for all cells in the current generation, you can then print out the next generation. Finally you can copy the next generation array to the current generation array, and perform the whole procedure again for the next generation.
To calculate the state of a cell in the next generation from it and its neighbours current state, you can use some simple binary manipulation. For example, say you were using the ruleset with Wolfram code 110 and you wanted to calculate the state in the next generation of a dead cell with two live neighbours i.e. 101. First, treat 101 as a binary number and convert it to decimal i.e. 5. Now right shift the rule number i.e. 110 by 5 and look at the value of the least significant bit i.e. 0. This is the state of the cell in the next generation. A complete set of pictures for each Wolfram code is given here. Rule 90 and Rule 110 are particular interesting.
Your program should handle dynamically allocated memory correctly i.e. free all dynamically allocated memory. As discussed in lectures, a good tool for assessing if a program has handled dynamic memory allocation correctly is valgrind. To check your program using valgrind you can type:
$ valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./task1
If your program has correctly handled dynamic memory allocation, the last line of output should read:
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started