Can this code be reviewed?
I've updated this question to be about code style only, as all of its answered focused on this aspect. For the codes function, see Review: C++ Algo - Function
'algo' is an algorithm thats dynamic. 'algo' instances can be created, asked to become random, mutated and also run. They can be set to remember changes they make to themselves between runs or not. They also output values, these could be used for anything from value sequences such as mathematical number sequences to controls for a bot in a game. It's straightforward to specify limits on memory or computation steps for each as well and needless to say are entirely sandboxed.
By sandboxed I mean that they only compute and produce output as described, they cannot for example use local or global variables, print to the console, #include or write to files.
'algo's can be used where algorithms need to be portable and must be only able to calculate/compute. There is no distinction between data and instructions in an 'algo'.
A use is as values for directed search for algorithms such as with evolutionary algorithms, MCTS or others.
Another is in a data file that includes algorithms, like an image that includes its own way to decompress itself, that can therefore be constructed using the specific image that is to be decompresed.
They are delibratly general, being a component that could be used in many contexts and conceptually simple, as a number is.
// by Alan Tennant
#include <iostream>
#include <vector>
#include <string>
#include <time.h> // for rand
class algo
{
public:
std::vector<unsigned short> code;
std::vector<unsigned int> output;
bool debug_info;
algo()
{
reset1(false);
instructions_p = 11;
}
void random(unsigned int size)
{
code.clear();
output.clear();
for(unsigned int n = 0; n < size; n++) {
code.push_back(rand() % instructions_p);}
reset1(true);
}
void run(
unsigned long most_run_time,
unsigned int largest_program,
unsigned int largest_output_size,
bool reset)
{
if (reset && !is_reset_p)
{
reset1(true);
output.clear();
code2 = code;
code_pos = 0;
}
is_reset_p = false;
size_t code_size = code2.size();
if (debug_info && !can_resume_p)
std::cout<<"can't resume, reset first"<<std::endl;
if(code_size == 0 || most_run_time == 0)
{
out_of_time_p = true;
out_of_space_p = false;
run_time_p = most_run_time;
}
else if (can_resume_p)
{
unsigned short instruction;
bool cont = true;
if(debug_info) {
std::cout<<"size: "<<code_size<<std::endl<<std::endl;}
while(cont)
{
instruction = code2[code_pos] % instructions_p;
if(debug_info) {std::cout<<code_pos<<", ";}
code_pos = (code_pos + 1) % code_size;
switch(instruction)
{
case 0:
if(debug_info) {std::cout<<"end";}
cont = false;
can_resume_p = false;
break;
case 1:
if(debug_info) {
std::cout<<"goto p1";}
code_pos = code2[(code_pos + 1) % code_size];
break;
case 2:
if(debug_info) {
std::cout<<"if value at p1 % 2 = 0 then goto p2";}
if(code2[code2[code_pos] % code_size] % 2 == 0) {
code_pos = code2[(code_pos + 1) % code_size];}
else {
code_pos += 2;}
break;
case 3:
if(debug_info) {std::cout<<"value at p1 = value at p2";}
code2[code2[code_pos] % code_size] =
code2[code2[(code_pos + 1) % code_size] % code_size];
code_pos += 2;
break;
case 4:
if(debug_info) {
std::cout<<"value at p1 = value at p2 + value at p3";}
code2[code2[code_pos] % code_size] = (
code2[code2[(code_pos + 1) % code_size] % code_size] +
code2[code2[(code_pos + 2) % code_size] % code_size]
) % USHRT_MAX;
code_pos += 3;
break;
case 5:
{
if(debug_info)
{std::cout<<"value at p1 = value at p2 - value at p3";}
long v1 =
(long)code2[code2[(code_pos + 1) % code_size] % code_size] -
code2[code2[(code_pos + 2) % code_size] % code_size];
code2[code2[code_pos] % code_size] = abs(v1) % USHRT_MAX;
code_pos += 3;
}
break;
case 6:
{
if(debug_info) {std::cout<<"toggle value at p1";}
size_t v1 = code2[code_pos] % code_size;
unsigned short v2 = code2[v1];
if(v2 == 0) {code2[v1] = 1;}
else {code2[v1] = 0;}
code_pos++;
}
break;
case 7:
if(debug_info) {
std::cout<<"output value at p1";}
output.push_back(code2[code2[code_pos] % code_size]);
code_pos++;
break;
case 8:
if(debug_info) {std::cout<<"increase size";}
code2.push_back(0);
break;
case 9:
{
if(debug_info) {std::cout<<"increment value at p1";}
size_t v1 = code2[code_pos] % code_size;
code2[v1] = (code2[v1] + 1) % USHRT_MAX;
code_pos++;
}
break;
case 10:
{
if(debug_info) {std::cout<<"decrement value at p1";}
size_t v1 = code2[code_pos] % code_size;
code2[v1] = abs((code2[v1] - 1) % USHRT_MAX);
code_pos++;
}
break;
}
if(debug_info) {std::cout<<std::endl;}
run_time_p++;
code_size = code2.size();
code_pos = code_pos % code_size;
if(run_time_p == most_run_time) {
cont = false; out_of_time_p = true;}
if(code_size > largest_program)
{
cont = false;
can_resume_p = false;
out_of_space_p = true;
if (debug_info)
std::cout<<"became too large"<<std::endl;
}
if(output.size() > largest_output_size)
{
cont = false;
can_resume_p = false;
output.pop_back();
if (debug_info)
std::cout<<"too much output"<<std::endl;
}
}
if (debug_info)
{
std::cout<<std::endl<<"size: "<<code_size<<std::endl<<
std::endl<<"output: "<<std::endl;
size_t output_size = output.size();
for (size_t t = 0; t < output_size; t++)
std::cout<<output[t]<<std::endl;
}
}
}
void mutate(unsigned int largest_program)
{
output.clear();
size_t size;
// special mutations
while(rand() % 4 != 0) // 3/4 chance
{
size = code.size();
if(rand() % 2 == 0) // 1/2 chance
{
// a bit of code is added to the end (would prefer inserted anywhere)
if(size < largest_program) {
code.push_back(rand() % instructions_p);}
}
else
{
// a bit of code is removed from the end (would prefer removed from anywhere)
if(size != 0)
code.pop_back();
}
// a section of code is moved, not yet implemented.
}
// mutate bits of the code
size = code.size();
if (size > 0)
{
unsigned int most_mutation = unsigned int(size * 0.1f);
if(most_mutation < 9)
most_mutation = 8;
unsigned int mutation = rand() % most_mutation;
for(unsigned int n = 0; n < mutation; n++)
code[rand() % size] = rand() % instructions_p;
}
reset1(true);
}
#pragma region
unsigned long run_time()
{
return run_time_p;
}
bool out_of_time()
{
return out_of_time_p;
}
bool out_of_space()
{
return out_of_space_p;
}
bool can_resume()
{
return can_resume_p;
}
bool is_reset()
{
return is_reset_p;
}
private:
bool can_resume_p, is_reset_p,
out_of_time_p, out_of_space_p;
unsigned int code_pos;
unsigned short instructions_p;
unsigned long run_time_p;
std::vector<unsigned short> code2;
void reset1(bool say)
{
out_of_time_p = false;
out_of_space_p = false;
run_time_p = 0;
code2 = code;
code_pos = 0;
can_resume_p = true;
is_reset_p = true;
if (say && debug_info)
std::cout<<"reset"<<std::endl;
}
#pragma endregion
};
void main()
{
srand((unsigned int)time(NULL));
algo a = algo();
a.random(50);
std::cout<<std::endl<<std::endl;
a.run(10, 100, 10, false);
std::cout<<std::endl<<std::endl;
a.run(10, 100, 10, false);
}
I've improved the code in some of the ways described, above is the original code, thank you for the feedback.
Its usual for me to prioritise in this way, but I'm avoiding function calls in the main loop of run because I've found in this program they made a big difference to performance. Apart from that I like HostileFork and Anders K idea of using numeric_limits::max() over USHRT_MAX.
I'm not keen on "int main()", but in the interests of conformity its been changed from "void main()".
cstdlib and ctime have replaced time.h
From the answers, GCC doesn't seem that good, but to support it I have changed unsigned int(size * 0.1) to static_cast(size * 0.1).