Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

See Randomizing and mutating algo class - style

I'm looking for a review of the functionality of this code, answers to the above question which also describes the code use focus on style instead.

Below is the updated code since the style feedback.

algo is an algorithm that is 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.

algos 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 decompressed.

They are deliberately general, being a component that could be used in many contexts and conceptually simple, as a number is.

// by Alan Tennant
// Nov 2012

#include <iostream>
#include <vector>
#include <string>
#include <cstdlib> // for rand and abs
#include <ctime>

class algo
{
    public:
        std::vector<unsigned short> code;
        std::vector<unsigned int> output;
        bool debug_info;

        algo()
        {
            reset1(false);
            instructions_p = 11;
            debug_info = true;
        }

        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 =
                    static_cast<unsigned int>(size * 0.1); // static_cast to support GCC
                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
};

int 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);
    return 0;
}
share|improve this question
    
"Below is the updated code since the style feedback": it still lacks many improvements suggested in the previous feedbacks (#pragma region is still there, you stil use USHRT_MAX...). –  Morwenn Mar 26 at 12:47
    
This is from some time ago and I'm not working on it anymore. I actioned much of the style feedback, but some I preferred to leave as before. –  alan2here Mar 27 at 19:00

1 Answer 1

It is a bit hard to tell what algo does. For one thing, some of its data members are all public, but they should all be private. There's also quite a bit of inline code, making it hard to read. For this, I'd recommend putting the implementation details in a separate file, and just have declarations in this file.

Your "gutted-out" class may look like this:

class algo
{
    private:
        std::vector<unsigned short> code;
        std::vector<unsigned int> output;
        bool debug_info;
        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);

    public:
        algo();
        void random(unsigned int size);
        void run(
            unsigned long most_run_time,
            unsigned int largest_program,
            unsigned int largest_output_size,
            bool reset);
        void mutate(unsigned int largest_program);

        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;
        }
};

From here, you can get a better idea of what it's really doing. You can easily focus on the definition and implementation separately. It'll also avoid having everything inlined, which is done automatically within a class declaration (but single-line functions, such as getters and setters, are still okay to have).

Misc.:

  • User-defined type names generally start with a capital letter (this separates them from variables and functions), so algo should be Algo.

  • Some of your names are unclear. Does code actually contain code? Does output contain data that will be outputted? What is code_pos?

  • If a member function doesn't modify any data members, it should be const. This would apply to all of the bool functions.

  • You don't need to invoke the default constructor yourself when creating an object:

    algo a = algo();
    

    It will already be invoked upon object-creation:

    algo a;
    
  • You have std::endl scattered around the code, which you don't really need. It also flushes the buffer, which is slower.

    If you need a newline, simply output a \n:

    std::cout << "This prints two newlines at the end\n\n";
    
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.