1 /* code.h 2 * Copyright © 2015, Brian Derr <brian@derrclan.com> 3 */ 4 5 #ifndef CODE_H 6 #define CODE_H 7 8 #include <algorithm> 9 #include <iterator> 10 #include <random> 11 #include <vector> 12 13 14 namespace code { 15 const int kNope = 0; 16 const int kAlmost = 1; 17 const int kNailedIt = 2; 18 const int kUsed = -1; 19 } 20 21 // The Code class creates the random code the user is to guess. 22 class Code { 23 public: 24 25 // Code(); 26 Code(int length); 27 Code(int length, unsigned int seed); 28 Code(std::vector<int> secret); // For unit testing. 29 30 void Create(); Get()31 std::vector<int> Get() const { return code_; }; Length()32 int Length() const { return length_; }; 33 34 // Where the magic happens. 35 // 36 // Compare the user-supplied guess provided as iterators to the secret code_. 37 // If the color and column match between the guess and the secret code then a 38 // code::kNailedIt is added to the return vector. If the color matches but not the 39 // column then code::kAlmost, and if there is no match for the guess then code::kNope. 40 // 41 // The result vector is returned sorted with the closest match at the 42 // beginning. 43 // 44 // Examples: 45 // guess = {0, 1, 1, 2}; 46 // code = {0, 1, 2, 3}; 47 // result = {code::kNailedIt, code::kNailedIt, code::kAlmost, code::kNope}; 48 // 49 // guess = {0, 1, 1, 2}; 50 // code = {1, 1, 2, 3}; 51 // result = {code::kNailedIt, code::kAlmost, code::kAlmost, code::kNope} 52 // template <typename Iterator> 53 template <class Iterator> IsCorrect(Iterator guess_start,Iterator guess_end)54 std::vector<int> IsCorrect(Iterator guess_start, Iterator guess_end) const { 55 if (std::equal(code_.begin(), code_.end(), guess_start)) { 56 // It's our lucky day! 57 std::vector<int> result(4, 2); 58 return result; 59 } 60 61 std::vector<int> code = code_; 62 std::vector<int> guess(guess_start, guess_end); 63 std::vector<int> result; 64 65 // First pass to mark all guesses that are fully correct. 66 for (unsigned int i = 0; i < guess.size(); i++) { 67 if (guess[i] == code[i]) { 68 result.push_back(code::kNailedIt); 69 guess[i] = code::kUsed; 70 code[i] = code::kUsed; 71 } 72 } 73 74 // Second pass to mark guesses that are the correct color but in the wrong location 75 // as well as those that are completely wrong. 76 for (int i = 0; i < 8; i++) { 77 auto guess_count = std::count(guess.begin(), guess.end(), i); 78 auto code_count = std::count(code.begin(), code.end(), i); 79 if (guess_count > 0 && code_count > 0) { 80 int difference = guess_count - code_count; 81 if (difference > 0) { 82 std::fill_n(std::back_inserter(result), difference, code::kNope); 83 std::fill_n(std::back_inserter(result), code_count, code::kAlmost); 84 } else { 85 std::fill_n(std::back_inserter(result), guess_count, code::kAlmost); 86 } 87 } 88 } 89 90 // We need to fill the result vector up with code::kNope's to the size expected. 91 std::fill_n(std::back_inserter(result), guess.size() - result.size(), code::kNope); 92 93 if (result.size() < 4 || result.size() > 6) { 94 // throw an exception? 95 } 96 std::sort(result.begin(), result.end(), std::greater<int>()); 97 return result; 98 } 99 100 private: 101 int GetRandomNumber(); 102 103 std::vector<int> code_; 104 std::uniform_int_distribution<int> dist_; 105 std::default_random_engine engine_; 106 int length_; 107 }; 108 109 #endif // CODE_H 110