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