Advertisement
Guest User

Untitled

a guest
Apr 21st, 2016
179
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.65 KB | None | 0 0
  1. #include <algorithm>
  2. #include <array>
  3. #include <chrono>
  4. #include <future>
  5. #include <iterator>
  6. #include <iostream>
  7. #include <ostream>
  8. #include <random>
  9. #include <stdexcept>
  10. #include <string>
  11. #include <thread>
  12. #include <vector>
  13.  
  14. namespace bunny_exercise
  15. {
  16.     using std::array;
  17.     using std::back_inserter;
  18.     using std::chrono::milliseconds;
  19.     using std::copy;
  20.     using std::count_if;
  21.     using std::endl;
  22.     using std::ostream;
  23.     using std::default_random_engine;
  24.     using std::find_if;
  25.     using std::for_each;
  26.     using std::logic_error;
  27.     using std::remove_if;
  28.     using std::shuffle;
  29.     using std::sort;
  30.     using std::string;
  31.     using std::this_thread::sleep_for;
  32.     using std::transform;
  33.     using std::uniform_int_distribution;
  34.     using std::vector;
  35.  
  36.     // Each bunny object must have Sex: Male, Female
  37.     //
  38.     enum class Sex { male, female };
  39.  
  40.     ostream& operator<<(ostream& os, const Sex sex)
  41.     {
  42.         switch (sex) {
  43.             case Sex::male:
  44.                 os << "m";
  45.                 break;
  46.  
  47.             case Sex::female:
  48.                 os << "f";
  49.                 break;
  50.  
  51.             default:
  52.                 throw logic_error{""};
  53.         }
  54.  
  55.         return os;
  56.     }
  57.  
  58.     // Convenience array to ease selecting a random value
  59.     const array<Sex, 2> sexes = { Sex::male, Sex::female };
  60.  
  61.     // Each bunny object must have color: white, brown, black, spotted
  62.     //
  63.     enum class Color { white, brown, black, spotted };
  64.  
  65.     ostream& operator<<(ostream& os, const Color color)
  66.     {
  67.         switch (color) {
  68.             case Color::white:
  69.                 os << "white";
  70.                 break;
  71.  
  72.             case Color::brown:
  73.                 os << "brown";
  74.                 break;
  75.  
  76.             case Color::black:
  77.                 os << "black";
  78.                 break;
  79.  
  80.             case Color::spotted:
  81.                 os << "spotted";
  82.                 break;
  83.  
  84.             default:
  85.                 throw logic_error{""};
  86.         }
  87.  
  88.         return os;
  89.     }
  90.  
  91.     // Convenience array to ease indexing and selecting a random value
  92.     const array<Color, 4> colors = { Color::white, Color::brown, Color::black, Color::spotted };
  93.  
  94.     // Each bunny object must have Name randomly chosen from a list of bunny names
  95.     //
  96.     const array<string, 12> names = {
  97.         "Babbitty", "Basil", "Benjamin", "Bigwig",
  98.         "Bionic", "Br'er", "Bunnicula", "Bunnsy",
  99.         "Janet", "Buster", "Cecily", "Cottontail"
  100.     };
  101.  
  102.     // Bunny objects
  103.     // There's no invariant that isn't already enforced by the type system,
  104.     // so a simple struct will suffice
  105.     //
  106.     struct Bunny {
  107.         Sex sex;
  108.         Color color;
  109.         unsigned int age;
  110.         string name;
  111.         bool is_radioactive_mutant_vampire;
  112.     };
  113.  
  114.     // If a bunny becomes older than 10 years old, it dies
  115.     // Radioactive vampire bunnies do not die until they reach age 50
  116.     //
  117.     const auto max_age = 10;
  118.     const auto radioactive_mutant_vampire_max_age = 50;
  119.  
  120.     auto is_alive(const Bunny& bunny)
  121.     {
  122.         return (
  123.             !bunny.is_radioactive_mutant_vampire && bunny.age < max_age ||
  124.              bunny.is_radioactive_mutant_vampire && bunny.age < radioactive_mutant_vampire_max_age
  125.         );
  126.     }
  127.  
  128.     // So long as there is at least one male age 2 or older,
  129.     // for each female bunny in the list age 2 or older, a new bunny is created each turn
  130.     // Radioactive vampire bunnies are excluded from regular breeding and do not count as adult bunnies
  131.     //
  132.     const auto breedable_min_age = 2;
  133.  
  134.     auto can_breed(const Bunny& bunny)
  135.     {
  136.         return is_alive(bunny) && bunny.age >= breedable_min_age && !bunny.is_radioactive_mutant_vampire;
  137.     }
  138.  
  139.     // The program should print all the bunnies details
  140.     //
  141.     ostream& operator<<(ostream& os, const Bunny& bunny)
  142.     {
  143.         if (bunny.is_radioactive_mutant_vampire) {
  144.             os << "Radioactive Mutant Vampire ";
  145.         }
  146.  
  147.         os << "Bunny " << bunny.name << ", " << bunny.age << "/" << bunny.sex;
  148.  
  149.         if (!is_alive(bunny)) {
  150.             os << ", is dead";
  151.         }
  152.  
  153.         return os;
  154.     }
  155.  
  156.     // Each bunny object must have
  157.     // Sex: Male, Female (random at creation 50/50)
  158.     // Color: white, brown, black, spotted (either inherited or randomly chosen)
  159.     // Name: randomly chosen at creation from a list of bunny names
  160.     // radioactive_mutant_vampire_bunny: true/false (decided at time of bunny creation 2% chance of true)
  161.     //
  162.     // This was almost just a pair of functions, but since they both need a random engine,
  163.     // it seemed better to scope that random engine to just these two functions
  164.     //
  165.     const auto percent_chance_born_radioactive = 2;
  166.  
  167.     class Builder
  168.     {
  169.         public:
  170.             auto make_random(Color color)
  171.             {
  172.                 const auto random_sex = sexes.at(random_sex_distribution_(random_engine_));
  173.                 const auto random_name = names.at(random_name_distribution_(random_engine_));
  174.                 const auto random_is_radioactive = random_radioactive_distribution_(random_engine_) <= percent_chance_born_radioactive;
  175.  
  176.                 return Bunny{random_sex, color, 0, random_name, random_is_radioactive};
  177.             }
  178.  
  179.             auto make_random()
  180.             {
  181.                 const auto random_color = colors.at(random_color_distribution_(random_engine_));
  182.  
  183.                 return make_random(random_color);
  184.             }
  185.  
  186.         private:
  187.             default_random_engine random_engine_;
  188.             uniform_int_distribution<decltype(colors)::size_type> random_color_distribution_{0, colors.size() - 1};
  189.             uniform_int_distribution<decltype(sexes)::size_type> random_sex_distribution_{0, sexes.size() - 1};
  190.             uniform_int_distribution<decltype(names)::size_type> random_name_distribution_{0, names.size() - 1};
  191.             uniform_int_distribution<> random_radioactive_distribution_{1, 100};
  192.     };
  193.  
  194.     // The program should print a list of all the bunnies in the colony each turn along w/ all the bunnies details, sorted by age
  195.     //
  196.     ostream& operator<<(ostream& os, const vector<Bunny>& bunnies)
  197.     {
  198.         // We need to sort the list but we don't want to alter the original array,
  199.         // so make an array of pointers
  200.         vector<const Bunny*> bunnies_ptrs;
  201.         transform(bunnies.begin(), bunnies.end(), back_inserter(bunnies_ptrs), [] (const Bunny& bunny) {
  202.             return &bunny;
  203.         });
  204.  
  205.         // Sort by age
  206.         sort(bunnies_ptrs.begin(), bunnies_ptrs.end(), [] (const Bunny* a, const Bunny* b) {
  207.             return a->age < b->age;
  208.         });
  209.  
  210.         os << "---- begin newborns\n";
  211.         for_each(bunnies_ptrs.begin(), bunnies_ptrs.end(), [&os] (const Bunny* bunny) {
  212.             if (bunny->age == 0) {
  213.                 os << *bunny << "\n";
  214.             }
  215.         });
  216.         os << "---- end newborns\n";
  217.  
  218.         os << "---- begin population\n";
  219.         for_each(bunnies_ptrs.begin(), bunnies_ptrs.end(), [&os] (const Bunny* bunny) {
  220.             os << *bunny << "\n";
  221.         });
  222.         os << "---- end population\n";
  223.  
  224.         os << "\n####\n" << endl;
  225.  
  226.         return os;
  227.     }
  228.  
  229.     // Operate on copy to favor immutability at the call site and to simplify concurrency
  230.     //
  231.     auto next_generation(vector<Bunny> bunnies, Builder* builder)
  232.     {
  233.         // If a radioactive mutant vampire bunny is born then each turn it will
  234.         // change exactly one non radioactive bunny into a radioactive vampire bunny
  235.         // But the problem description didn't specify how we should pick the bunnies to infect
  236.         // I could just iterate through the array left to right, but then I'll always pick the oldest bunnies first
  237.         // I'd rather pick bunnies to infect at random
  238.         // std::sample would be perfect, but it isn't available yet in compilers,
  239.         // so instead I'll shuffle the bunnies then iterate left to right
  240.         // But we don't want to alter the original array, so make an array of pointers
  241.         vector<Bunny*> bunnies_ptrs;
  242.         transform(bunnies.begin(), bunnies.end(), back_inserter(bunnies_ptrs), [] (Bunny& bunny) {
  243.             return &bunny;
  244.         });
  245.         shuffle(bunnies_ptrs.begin(), bunnies_ptrs.end(), default_random_engine{});
  246.  
  247.         // If a radioactive mutant vampire bunny is born then each turn it will
  248.         // change exactly one non radioactive bunny into a radioactive vampire bunny
  249.         // If there are two radioactive mutant vampire bunnies two bunnies will be changed each turn and so on...
  250.         auto n_radioactive_bunnies = count_if(bunnies.begin(), bunnies.end(), [] (const Bunny& bunny) {
  251.             return bunny.is_radioactive_mutant_vampire;
  252.         });
  253.         for_each(bunnies_ptrs.begin(), bunnies_ptrs.end(), [&n_radioactive_bunnies] (Bunny* bunny) {
  254.             if (n_radioactive_bunnies > 0 && is_alive(*bunny) && !bunny->is_radioactive_mutant_vampire) {
  255.                 bunny->is_radioactive_mutant_vampire = true;
  256.                 --n_radioactive_bunnies;
  257.             }
  258.         });
  259.  
  260.         // So long as there is at least one male age 2 or older,
  261.         // for each female bunny in the list age 2 or older, a new bunny is created each turn
  262.         // If there was 1 adult male and 3 adult female bunnies, three new bunnies would be born each turn
  263.         auto has_breedable_male = find_if(bunnies.begin(), bunnies.end(), [] (const Bunny& bunny) {
  264.             return can_breed(bunny) && bunny.sex == Sex::male;
  265.         }) != bunnies.end();
  266.  
  267.         vector<Bunny> new_bunnies;
  268.         if (has_breedable_male) {
  269.             for_each(bunnies.begin(), bunnies.end(), [&new_bunnies, builder] (const Bunny& bunny) {
  270.                 // Filter for breed-able females
  271.                 if (!can_breed(bunny) || bunny.sex != Sex::female) return;
  272.  
  273.                 // New bunnies born should be the same color as their mother
  274.                 new_bunnies.push_back(builder->make_random(bunny.color));
  275.             });
  276.         }
  277.  
  278.         // Each turn the bunnies age 1 year (the newborns haven't gone a turn yet)
  279.         for_each(bunnies.begin(), bunnies.end(), [] (Bunny& bunny) {
  280.             ++bunny.age;
  281.         });
  282.  
  283.         // If a bunny becomes older than 10 years old, it dies
  284.         remove_if(bunnies.begin(), bunnies.end(), [] (const Bunny& bunny) {
  285.             return !is_alive(bunny);
  286.         });
  287.  
  288.         // Add the newborns to the population
  289.         copy(new_bunnies.begin(), new_bunnies.end(), back_inserter(bunnies));
  290.  
  291.         // Pretend long complex operation
  292.         // This gives me an excuse to play with concurrency
  293.         sleep_for(milliseconds(4000));
  294.  
  295.         return bunnies;
  296.     }
  297. }
  298.  
  299. int main(const int argc, const char* const argv[])
  300. {
  301.     using bunny_exercise::Builder;
  302.     using bunny_exercise::Bunny;
  303.     using bunny_exercise::next_generation;
  304.     using std::async;
  305.     using std::chrono::milliseconds;
  306.     using std::cout;
  307.     using std::this_thread::sleep_for;
  308.     using std::vector;
  309.  
  310.     Builder builder;
  311.  
  312.     // At program initialization 5 bunnies must be created and given random colors
  313.     vector<Bunny> bunnies;
  314.     for (auto n_repeat = 5; n_repeat > 0; --n_repeat) {
  315.         bunnies.push_back(builder.make_random());
  316.     }
  317.     cout << bunnies;
  318.  
  319.     do {
  320.         // Compute the next generation concurrently with the 5s waiting period,
  321.         // otherwise we'll be waiting the 5s plus however long this computation takes
  322.         auto future_bunnies = async(next_generation, bunnies, &builder);
  323.  
  324.         sleep_for(milliseconds(5000));
  325.  
  326.         bunnies = future_bunnies.get();
  327.         cout << bunnies;
  328.     } while (true);
  329.  
  330.     return 0;
  331. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement