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