1 /* braincurses.cpp
2  * Copyright © 2002–2015, Brian Derr <brian@derrclan.com>
3  */
4 
5 #include "braincurses.h"
6 
7 #include <cstdlib>
8 
9 #include "code.h"
10 
11 
12 // Maps the x-coordinates (within a WINDOW) for different values of code_length_.
13 const std::unordered_map<int, std::vector<int>> codePosition = {
14     {4, {2, 6, 10, 14}},
15     {5, {2, 5, 8, 11, 14}},
16     {6, {3, 5, 7, 9, 11, 13}}
17 };
18 
19 
Braincurses(int code_length,int guesses)20 Braincurses::Braincurses(int code_length, int guesses) :
21     code_(Code(code_length)), guesses_(guesses), initialized_(false) {
22   InitializeNcurses();
23 }
24 
InitializeNcurses()25 void Braincurses::InitializeNcurses() {
26   initscr();
27 
28   if (has_colors() == FALSE) {
29     // TODO: throw an exception?
30   }
31 
32   start_color();
33   if (COLORS < 8) {
34     // TODO: throw an exception?
35   }
36   use_default_colors();
37 
38   // Only using the first eight color pairs.
39   for (int i = 0; i < 8; i++) {
40     if (i == 6) {
41       // COLOR_CYAN is hard to distinguish from COLOR_BLUE; replace it with COLOR_WHITE.
42       init_pair(i, i+1, -1);
43     } else {
44       init_pair(i, i, -1);
45     }
46   }
47 
48   cbreak();
49   noecho();
50   intrflush(stdscr, FALSE);
51   curs_set(1);
52 
53   windows_.emplace(kHeaderWindow, CreateWindow(3, 21, 0, 0));
54   windows_.emplace(kMarkerWindow, CreateWindow(17, 17, 3, 0));
55   windows_.emplace(kGuessWindow, CreateWindow(17, 17, 3, 21));
56   windows_.emplace(kCodeWindow, CreateWindow(3, 17, 0, 21));
57   windows_.emplace(kWatermarkWindow, CreateWindow(17, 4, 3, 17));
58   windows_.emplace(kInputWindow, CreateWindow(3, 60, 20, 0));
59   windows_.emplace(kInfoWindow, CreateWindow(20, 22, 0, 38));
60 
61   initialized_ = true;
62 }
63 
PrepareBoard()64 void Braincurses::PrepareBoard() {
65   mvwaddstr(windows_[kHeaderWindow], 1, 5, kGameName.c_str());
66   mvwaddstr(windows_[kInfoWindow], 1, 2, "Colors: ");
67   wattron(windows_[kInfoWindow], A_BOLD);
68 
69   wattron(windows_[kInfoWindow], COLOR_PAIR(COLOR_RED));
70   mvwaddstr(windows_[kInfoWindow], 2, 2, "RED");
71   wattroff(windows_[kInfoWindow], COLOR_PAIR(COLOR_RED));
72 
73   waddstr(windows_[kInfoWindow], ", ");
74 
75   wattron(windows_[kInfoWindow], COLOR_PAIR(COLOR_BLUE));
76   waddstr(windows_[kInfoWindow], "BLUE");
77   wattroff(windows_[kInfoWindow], COLOR_PAIR(COLOR_BLUE));
78 
79   waddstr(windows_[kInfoWindow], ", ");
80 
81   mvwaddstr(windows_[kInfoWindow], 3, 2, "WHITE, ");
82 
83   wattron(windows_[kInfoWindow], COLOR_PAIR(COLOR_GREEN));
84   waddstr(windows_[kInfoWindow], "GREEN");
85   wattroff(windows_[kInfoWindow], COLOR_PAIR(COLOR_GREEN));
86 
87   waddstr(windows_[kInfoWindow], ", ");
88 
89   wattron(windows_[kInfoWindow], COLOR_PAIR(COLOR_YELLOW));
90   mvwaddstr(windows_[kInfoWindow], 4, 2, "YELLOW");
91   wattroff(windows_[kInfoWindow], COLOR_PAIR(COLOR_YELLOW));
92 
93   waddstr(windows_[kInfoWindow], ", ");
94 
95   wattron(windows_[kInfoWindow], COLOR_PAIR(COLOR_MAGENTA));
96   waddstr(windows_[kInfoWindow], "MAGENTA");
97   wattroff(windows_[kInfoWindow], COLOR_PAIR(COLOR_MAGENTA));
98 
99   wattroff(windows_[kInfoWindow], A_BOLD);
100 
101 #ifdef DEBUG
102   DisplayCode(true);
103 #else
104   DisplayCode(false);
105 #endif
106 
107   char guessLabel[3];
108   for (int i = 1; i <= guesses_; i++) {
109     snprintf(guessLabel, 3, "%2d", i);
110     mvwaddstr(windows_[kWatermarkWindow], 16 - i, 1, guessLabel);
111   }
112 
113   wmove(windows_[kInputWindow], 1, 15);
114   wnoutrefresh(windows_[kHeaderWindow]);
115   wnoutrefresh(windows_[kCodeWindow]);
116   wnoutrefresh(windows_[kInfoWindow]);
117   wnoutrefresh(windows_[kWatermarkWindow]);
118   doupdate();
119 }
120 
CreateWindow(int height,int width,int starty,int startx)121 WINDOW* Braincurses::CreateWindow(int height, int width, int starty, int startx) {
122   WINDOW* window;
123 
124   window = newwin(height, width, starty, startx);
125   box(window, 0, 0);
126   wrefresh(window);
127 
128   return window;
129 }
130 
CleanUpWindow(WINDOW * window)131 void Braincurses::CleanUpWindow(WINDOW* window) {
132   werase(window);
133   box(window, 0, 0);
134   wrefresh(window);
135 }
136 
WipeBoard()137 void Braincurses::WipeBoard() {
138   for (auto kv : windows_) {
139     CleanUpWindow(kv.second);
140   }
141 }
142 
GetInput()143 std::vector<int> Braincurses::GetInput() {
144   std::string delStr (kInputLength, ' ');
145   std::vector<int> guessInput (code_.Length(), -1);
146   int column[6] = {2, 11, 20, 29, 38, 47};
147   int input;
148 
149   auto local_window = windows_[kInputWindow];
150 
151   keypad(local_window, TRUE);
152   int x = 0;
153   while (true) {
154     input = mvwgetch(local_window, 1, column[x]);
155     switch (input) {
156       case 'r':
157         mvwaddstr(local_window, 1, column[x], delStr.c_str());
158         mvwaddstr(local_window, 1, column[x], "red");
159         guessInput[x] = COLOR_RED;
160         x++;
161         break;
162       case 'g':
163         mvwaddstr(local_window, 1, column[x], delStr.c_str());
164         mvwaddstr(local_window, 1, column[x], "green");
165         guessInput[x] = COLOR_GREEN;
166         x++;
167         break;
168       case 'y':
169         mvwaddstr(local_window, 1, column[x], delStr.c_str());
170         mvwaddstr(local_window, 1, column[x], "yellow");
171         guessInput[x] = COLOR_YELLOW;
172         x++;
173         break;
174       case 'b':
175         mvwaddstr(local_window, 1, column[x], delStr.c_str());
176         mvwaddstr(local_window, 1, column[x], "blue");
177         guessInput[x] = COLOR_BLUE;
178         x++;
179         break;
180       case 'm':
181         mvwaddstr(local_window, 1, column[x], delStr.c_str());
182         mvwaddstr(local_window, 1, column[x], "magenta");
183         guessInput[x] = COLOR_MAGENTA;
184         x++;
185         break;
186       case 'w':
187         mvwaddstr(local_window, 1, column[x], delStr.c_str());
188         mvwaddstr(local_window, 1, column[x], "white");
189         guessInput[x] = COLOR_CYAN;  // Really COLOR_WHITE
190         x++;
191         break;
192       case KEY_LEFT:
193         if (x > 0) {
194           x--;
195           wmove(local_window, 1, column[x]);
196         }
197         break;
198       case KEY_RIGHT:
199         if (x < code_.Length()) {
200           wmove(local_window, 1, column[x]);
201           x++;
202         }
203         break;
204       case KEY_BACKSPACE:
205       case KEY_DC:
206         if (x > 0 && x <= code_.Length()) {
207           x--;
208           mvwaddstr(local_window, 1, column[x], delStr.c_str());
209           guessInput[x] = -1;
210         }
211         break;
212       default:
213         break;
214     }
215     wrefresh(local_window);
216     if (x == code_.Length()) {
217       break;
218     }
219   }
220   CleanUpWindow(local_window);
221   return guessInput;
222 }
223 
DisplayGuess(int y,std::vector<int> guess)224 void Braincurses::DisplayGuess(int y, std::vector<int> guess) {
225   auto local_window = windows_[kGuessWindow];
226 
227   y = 15 - y;
228 
229   wattron(local_window, A_BOLD);
230   int i = 0;
231   for (auto x : codePosition.at(guess.size())) {
232     mvwaddch(local_window, y, x, 'X' | COLOR_PAIR(guess[i]));
233     i++;
234   }
235   wattroff(local_window, A_BOLD);
236   wrefresh(local_window);
237 }
238 
DisplayMarkers(int y,std::vector<int> correct)239 void Braincurses::DisplayMarkers(int y, std::vector<int> correct) {
240   auto local_window = windows_[kMarkerWindow];
241 
242   y = 15 - y;
243 
244   wattron(local_window, A_BOLD);
245 
246   std::vector<int> x_positions = codePosition.at(code_.Length());
247   for (int i = 0; i < code_.Length(); i++) {
248     int marker = correct[i];
249     int x = x_positions[i];
250 
251     if (marker == 2) {  // NAILED_IT
252       mvwaddch(local_window, y, x, 'X' | COLOR_PAIR(COLOR_RED));
253       continue;
254     } else if (marker == 1) {  // ALMOST
255       mvwaddch(local_window, y, x, 'X' | COLOR_PAIR(COLOR_WHITE));
256       continue;
257     }
258   }
259   wattroff(local_window, A_BOLD);
260   wrefresh(local_window);
261 }
262 
IsWinner(std::vector<int> correct)263 bool Braincurses::IsWinner(std::vector<int> correct) {
264   bool winner = true;
265   for (unsigned i = 0; i < correct.size(); i++) {
266     if (correct[i] != 2) {
267       winner = false;
268       break;
269     }
270   }
271   return winner;
272 }
273 
GameOverPlayAgain(bool winner)274 bool Braincurses::GameOverPlayAgain(bool winner) {
275   CleanUpWindow(windows_[kInputWindow]);
276   if (winner) {
277     mvwaddstr(windows_[kInputWindow], 1, 1, "You win! Congratulations!");
278   } else {
279     mvwaddstr(windows_[kInputWindow], 1, 1, "You ran out of turns; better luck next time.");
280   }
281   wrefresh(windows_[kInputWindow]);
282   return PlayAgain();
283 }
284 
DisplayCode(bool colored)285 void Braincurses::DisplayCode(bool colored) {
286   auto local_window = windows_[kCodeWindow];
287 
288   int i = 0;
289   for (auto x : codePosition.at(code_.Length())) {
290 #ifdef DEBUG
291     mvwaddch(local_window, 1, x, 'X' | COLOR_PAIR(code_.Get()[i]) | A_BOLD);
292 #else
293     if (colored) {
294       mvwaddch(local_window, 1, x, 'X' | COLOR_PAIR(code_.Get()[i]) | A_BOLD);
295     } else {
296       mvwaddch(local_window, 1, x, 'X');
297     }
298 #endif
299     i++;
300   }
301   wrefresh(local_window);
302 }
303 
PlayAgain()304 bool Braincurses::PlayAgain() {
305   CleanUpWindow(windows_[kInputWindow]);
306   mvwaddstr(windows_[kInputWindow], 1, 1, "Would you like to play again? ([Y]/n) ");
307   wrefresh(windows_[kInputWindow]);
308 
309   int again = tolower(wgetch(windows_[kInputWindow]));
310   return (again == 'y' || again == '\n' ? true : false);
311 }
312 
PlayGame()313 bool Braincurses::PlayGame() {
314   code_.Create();
315   WipeBoard();
316   PrepareBoard();
317 
318   bool winner = false;
319   std::vector<int> correct;
320   for (int i = 0; i < guesses_; i++) {
321     std::vector<int> guess = GetInput();
322     correct = code_.IsCorrect(guess.begin(), guess.end());
323 
324     DisplayGuess(i, guess);
325     DisplayMarkers(i, correct);
326     if (IsWinner(correct)) {
327       winner = true;
328       break;
329     }
330   }
331   DisplayCode(true);
332   return winner;
333 }
334