SHOW:
|
|
- or go back to the newest paste.
1 | /////////////////////////////////////////////////////////////////////////////// | |
2 | // Grid Game | |
3 | /////////////////////////////////////////////////////////////////////////////// | |
4 | // Grid game board program that lets users move a cursor around on a board | |
5 | /////////////////////////////////////////////////////////////////////////////// | |
6 | ||
7 | // Small define for if project were in multiple files | |
8 | //#define USE_SEPARATE_FILES | |
9 | ||
10 | #ifndef USE_SEPARATE_FILES | |
11 | #include <stdio.h> // For I/O | |
12 | #include <stdlib.h> // For rand | |
13 | #include <time.h> // For seeding Rand | |
14 | #include <string> // For memset | |
15 | #endif | |
16 | ||
17 | - | #define USE_SEPARATE_FILES |
17 | + | |
18 | #if defined _WIN32 || defined __CYGWIN__ | |
19 | #include <windows.h> | |
20 | #endif | |
21 | ||
22 | ||
23 | #ifdef USE_SEPARATE_FILES | |
24 | #include "RenderContext.h" | |
25 | #include "I_Drawable.h" | |
26 | #include "GameBoard.h" | |
27 | #ifdef WIN32 | |
28 | #include "CmdPromptHelper.h" | |
29 | #endif | |
30 | #else | |
31 | ||
32 | // RenderContext | |
33 | #ifndef USE_SEPARATE_FILES | |
34 | // Class that acts a canvas for things to draw to | |
35 | class RenderContext { | |
36 | private: | |
37 | int m_width, m_height; // Width and Height of this canvas | |
38 | char* m_renderBuffer; // Array to hold "pixels" of canvas | |
39 | char m_clearChar; // What to clear the array to | |
40 | ||
41 | public: | |
42 | // Empty Constructor | |
43 | RenderContext() : m_width(50), m_height(20), m_clearChar(' ') { | |
44 | m_renderBuffer = new char[m_width * m_height]; | |
45 | } | |
46 | ||
47 | // Construct to specify width and height of canvas | |
48 | RenderContext(int width, int height) : m_width(width), m_height(height), m_clearChar(' ') { | |
49 | m_renderBuffer = new char[m_width * m_height]; | |
50 | } | |
51 | ||
52 | // Destructor | |
53 | ~RenderContext() { | |
54 | if (m_renderBuffer) { | |
55 | delete m_renderBuffer; | |
56 | } | |
57 | } | |
58 | ||
59 | // Get a specific "pixel" on a canvas | |
60 | char getContentAt(int x, int y) { | |
61 | if (((0 <= x) && (x < m_width)) && ((0 <= y) && (y < m_height))) { | |
62 | return m_renderBuffer[(x + (y * m_width))]; | |
63 | } | |
64 | } | |
65 | ||
66 | // Fill a specific "pixel" on the canvas | |
67 | void setContentAt(int x, int y, char val) { | |
68 | if (((0 <= x) && (x < m_width)) && ((0 <= y) && (y < m_height))) { | |
69 | m_renderBuffer[(x + (y * m_width))] = val; | |
70 | } | |
71 | } | |
72 | ||
73 | // Change what this canvas is flooded with when cleared | |
74 | void setClearChar(char clearChar) { | |
75 | m_clearChar = clearChar; | |
76 | } | |
77 | ||
78 | // Paint the canvas to the shell | |
79 | void render() { | |
80 | int row, column; | |
81 | for (row = 0; row < m_height; row++) { | |
82 | for (column = 0; column < m_width; column++) { | |
83 | printf("%c", getContentAt(column, row)); | |
84 | } | |
85 | printf("\n"); | |
86 | } | |
87 | } | |
88 | ||
89 | // Clear the canvas | |
90 | void clear() { | |
91 | memset(m_renderBuffer, m_clearChar, m_width * m_height); | |
92 | } | |
93 | }; | |
94 | #endif | |
95 | ||
96 | // I_Drawable | |
97 | #ifndef USE_SEPARATE_FILES | |
98 | // Interface that allows objects to draw to the canvas | |
99 | class I_Drawable { | |
100 | public: | |
101 | // Virtual function that draws to the canvas | |
102 | virtual void draw(RenderContext&) = 0; | |
103 | }; | |
104 | #endif | |
105 | ||
106 | // Marker | |
107 | #ifndef USE_SEPARATE_FILES | |
108 | // Class representing the cursor that selects objects on a game board | |
109 | class Marker : public I_Drawable { | |
110 | private: | |
111 | int m_xPos, m_yPos; // Position of cursor | |
112 | public: | |
113 | // Empty constructor | |
114 | Marker() : m_xPos(0), m_yPos(0) { | |
115 | } | |
116 | // Parameterized constructor allowing for setting of initial position | |
117 | Marker(int xPos, int yPos) : m_xPos(xPos), m_yPos(yPos) { | |
118 | } | |
119 | // Draw the cursor to the canvas | |
120 | void draw(RenderContext& renderTarget) { | |
121 | ||
122 | // Adjust marker by board spacing | |
123 | // (This is kind of a hack and should be changed) | |
124 | int tmpX, tmpY; | |
125 | tmpX = ((m_xPos * 5) + 1); | |
126 | tmpY = ((m_yPos * 3) + 1); | |
127 | ||
128 | // Set surrounding elements | |
129 | renderTarget.setContentAt(tmpX - 0, tmpY - 1, '-'); | |
130 | renderTarget.setContentAt(tmpX - 1, tmpY - 0, '|'); | |
131 | renderTarget.setContentAt(tmpX - 0, tmpY + 1, '-'); | |
132 | renderTarget.setContentAt(tmpX + 1, tmpY - 0, '|'); | |
133 | } | |
134 | // Get x position | |
135 | int getXPos() { | |
136 | return m_xPos; | |
137 | } | |
138 | // Get y position | |
139 | int getYPos() { | |
140 | return m_yPos; | |
141 | } | |
142 | // Set x position | |
143 | void setXPos(int xPos) { | |
144 | m_xPos = xPos; | |
145 | } | |
146 | // Set y position | |
147 | void setYPos(int yPos) { | |
148 | m_yPos = yPos; | |
149 | } | |
150 | }; | |
151 | #endif | |
152 | ||
153 | // MoveDirection | |
154 | #ifndef USE_SEPARATE_FILES | |
155 | // Enumeration to hold directions a cursor can move | |
156 | typedef enum { | |
157 | MD_UP, | |
158 | MD_DOWN, | |
159 | MD_LEFT, | |
160 | MD_RIGHT | |
161 | } MoveDirection; | |
162 | #endif | |
163 | ||
164 | // GameBoard | |
165 | #ifndef USE_SEPARATE_FILES | |
166 | // Class to represent the board of a game being played | |
167 | class GameBoard : public I_Drawable { | |
168 | private: | |
169 | int m_width, m_height; // Width and height of the board | |
170 | int m_verticalSpacing, m_horizontalSpacing; // Spaces between each element on the board | |
171 | Marker m_marker; // The cursor that will draw on this board | |
172 | int* m_board; // Array of elements on this board | |
173 | ||
174 | // Set a value at a spot on this board | |
175 | // (There is no actual padding on the board, so do not include vertical/horizontal spacing) | |
176 | void setAtPos(int x, int y, int val) { | |
177 | if (((0 <= x) && (x < m_width)) && ((0 <= y) && (x < m_height))) { | |
178 | m_board[(x + (y * m_width))] = val; | |
179 | } | |
180 | } | |
181 | ||
182 | // Actually create the board | |
183 | void generateBoard() { | |
184 | int row, column, randomNumber, valToLeft, valToTop; | |
185 | ||
186 | // Iterate over all rows and columns | |
187 | for (row = 0; row < m_height; row++) { | |
188 | for (column = 0; column < m_width; column++) { | |
189 | // Get the previous elements | |
190 | valToLeft = getAtPos(column - 1, row); | |
191 | valToTop = getAtPos(column, row - 1); | |
192 | ||
193 | // Generate random numbers until we have one | |
194 | // that is not the same as an adjacent element | |
195 | do { | |
196 | randomNumber = (2 + (rand() % 7)); | |
197 | } while ((valToLeft == randomNumber) || (valToTop == randomNumber)); | |
198 | setAtPos(column, row, randomNumber); | |
199 | } | |
200 | } | |
201 | } | |
202 | ||
203 | public: | |
204 | // Empty Constructor | |
205 | GameBoard() : m_width(10), m_height(10), m_verticalSpacing(5), m_horizontalSpacing(3), m_marker(Marker()) { | |
206 | m_board = new int[m_width * m_height]; | |
207 | generateBoard(); | |
208 | } | |
209 | ||
210 | // Constructor that takes a width and height of a board | |
211 | GameBoard(int width, int height) : m_width(width), m_height(height), m_verticalSpacing(5), m_horizontalSpacing(3), m_marker(Marker()) { | |
212 | m_board = new int[m_width * m_height]; | |
213 | generateBoard(); | |
214 | } | |
215 | ||
216 | // Destructor | |
217 | ~GameBoard() { | |
218 | if (m_board) { | |
219 | delete m_board; | |
220 | } | |
221 | } | |
222 | ||
223 | // Function to get an element on the board | |
224 | // I would say overload operator() but it's often overkill | |
225 | // and can lead to hard to find bugs | |
226 | int getAtPos(int x, int y) { | |
227 | if (((0 <= x) && (x < m_width)) && ((0 <= y) && (x < m_height))) { | |
228 | return m_board[(x + (y * m_width))]; | |
229 | } | |
230 | } | |
231 | ||
232 | // Function to draw to the canvas | |
233 | void draw(RenderContext& renderTarget) { | |
234 | int row, column; | |
235 | char buffer[8]; | |
236 | ||
237 | // Iterate over every element | |
238 | for (row = 0; row < m_height; row++) { | |
239 | for (column = 0; column < m_width; column++) { | |
240 | ||
241 | // Convert the integer to a char | |
242 | sprintf(buffer, "%d", getAtPos(column, row)); | |
243 | ||
244 | // Set the canvas "pixel" to the char at the | |
245 | // desired position including the padding | |
246 | renderTarget.setContentAt( | |
247 | ((column * m_verticalSpacing) + 1), | |
248 | ((row * m_horizontalSpacing) + 1), | |
249 | buffer[0]); | |
250 | } | |
251 | } | |
252 | ||
253 | // Draw the marker | |
254 | m_marker.draw(renderTarget); | |
255 | } | |
256 | ||
257 | // Function to move the cursor around depending on input | |
258 | // (Does not allow the cursor to move off of the board) | |
259 | void handleInput(MoveDirection moveDirection) { | |
260 | switch (moveDirection) { | |
261 | case MD_UP: | |
262 | if (m_marker.getYPos() > 0) { | |
263 | m_marker.setYPos(m_marker.getYPos() - 1); | |
264 | } | |
265 | break; | |
266 | case MD_DOWN: | |
267 | if (m_marker.getYPos() < m_height - 1) { | |
268 | m_marker.setYPos(m_marker.getYPos() + 1); | |
269 | } | |
270 | break; | |
271 | case MD_LEFT: | |
272 | if (m_marker.getXPos() > 0) { | |
273 | m_marker.setXPos(m_marker.getXPos() - 1); | |
274 | } | |
275 | break; | |
276 | case MD_RIGHT: | |
277 | if (m_marker.getXPos() < m_width - 1) { | |
278 | m_marker.setXPos(m_marker.getXPos() + 1); | |
279 | } | |
280 | break; | |
281 | } | |
282 | } | |
283 | ||
284 | // Get the width of the board | |
285 | int getWidth() { | |
286 | return m_width; | |
287 | } | |
288 | ||
289 | // Get the height of the board | |
290 | int getHeight() { | |
291 | return m_height; | |
292 | } | |
293 | }; | |
294 | #endif | |
295 | ||
296 | // CmdPromptHelper | |
297 | #ifndef USE_SEPARATE_FILES | |
298 | // This class is only useful if we are building on windows | |
299 | #ifdef WIN32 | |
300 | ||
301 | // Class to help with drawing on the windows command prompt | |
302 | class CmdPromptHelper { | |
303 | private: | |
304 | DWORD inMode; // Attributes of std::in before we change them | |
305 | DWORD outMode; // Attributes of std::out before we change them | |
306 | HANDLE hstdin; // Handle to std::in | |
307 | HANDLE hstdout; // Handle to std::out | |
308 | public: | |
309 | // Empty constructor | |
310 | CmdPromptHelper() { | |
311 | // Get a handle to input of prompt | |
312 | hstdin = GetStdHandle(STD_INPUT_HANDLE); | |
313 | if ((hstdin == INVALID_HANDLE_VALUE) | |
314 | || !GetConsoleMode(hstdin, &inMode) | |
315 | || !SetConsoleMode(hstdin, 0)) { | |
316 | return; | |
317 | } | |
318 | ||
319 | // Get handle for output of prompt | |
320 | hstdout = GetStdHandle(STD_OUTPUT_HANDLE); | |
321 | if ((hstdout == INVALID_HANDLE_VALUE) | |
322 | || !GetConsoleMode(hstdout, &outMode)) { | |
323 | return; | |
324 | } | |
325 | } | |
326 | ||
327 | // Revert the console | |
328 | void reset() { | |
329 | SetConsoleMode(hstdin, inMode); | |
330 | } | |
331 | ||
332 | // See what key is pressed by the user and return it | |
333 | WORD getKeyPress() { | |
334 | if (hstdin != INVALID_HANDLE_VALUE) { | |
335 | DWORD count; | |
336 | - | // Return key |
336 | + | |
337 | ||
338 | // Get Key Press | |
339 | ReadConsoleInput(hstdin, &inrec, 1, &count); | |
340 | ||
341 | // Return key only if it is key down | |
342 | if (inrec.Event.KeyEvent.bKeyDown) { | |
343 | return inrec.Event.KeyEvent.wVirtualKeyCode; | |
344 | } | |
345 | else{ | |
346 | return 0; | |
347 | } | |
348 | ||
349 | // Flush input | |
350 | FlushConsoleInputBuffer(hstdin); | |
351 | } else { | |
352 | return 0; | |
353 | } | |
354 | } | |
355 | ||
356 | // Flood the console with empty space so that we can | |
357 | // simulate single buffering (I have no idea how to double buffer this) | |
358 | void clearScreen() { | |
359 | if (hstdout != INVALID_HANDLE_VALUE) { | |
360 | CONSOLE_SCREEN_BUFFER_INFO csbi; | |
361 | DWORD cellCount; // How many cells to paint | |
362 | DWORD count; // How many we painted | |
363 | COORD homeCoord = {0, 0}; // Where to put the cursor when we're done | |
364 | ||
365 | // Get console info | |
366 | if (!GetConsoleScreenBufferInfo(hstdout, &csbi)) { | |
367 | return; | |
368 | } | |
369 | ||
370 | // Get cell count | |
371 | cellCount = csbi.dwSize.X * csbi.dwSize.Y; | |
372 | ||
373 | // Fill the screen with spaces | |
374 | FillConsoleOutputCharacter( | |
375 | hstdout, | |
376 | (TCHAR) ' ', | |
377 | cellCount, | |
378 | homeCoord, | |
379 | &count | |
380 | ); | |
381 | ||
382 | // Set cursor position | |
383 | SetConsoleCursorPosition(hstdout, homeCoord); | |
384 | } | |
385 | } | |
386 | }; | |
387 | #endif | |
388 | ||
389 | #endif | |
390 | ||
391 | #endif | |
392 | // Entry point of program | |
393 | int main() { | |
394 | // Create a game board | |
395 | GameBoard gb(5, 5); | |
396 | ||
397 | // Create something to render to | |
398 | RenderContext rc(25, 15); | |
399 | rc.setClearChar('.'); | |
400 | rc.clear(); | |
401 | ||
402 | // If on windows, we can animate | |
403 | #ifdef WIN32 | |
404 | // Make a helper | |
405 | CmdPromptHelper cph; | |
406 | ||
407 | // Variable to hold key presses | |
408 | WORD key; | |
409 | ||
410 | // Play Game | |
411 | do { | |
412 | // Draw the board to the render object | |
413 | gb.draw(rc); | |
414 | ||
415 | // Draw the render object | |
416 | rc.render(); | |
417 | ||
418 | // Get Key Press | |
419 | key = cph.getKeyPress(); | |
420 | ||
421 | // Handle the key press | |
422 | switch (key) { | |
423 | case VK_UP: | |
424 | gb.handleInput(MD_UP); | |
425 | break; | |
426 | case VK_DOWN: | |
427 | gb.handleInput(MD_DOWN); | |
428 | break; | |
429 | case VK_LEFT: | |
430 | gb.handleInput(MD_LEFT); | |
431 | break; | |
432 | case VK_RIGHT: | |
433 | gb.handleInput(MD_RIGHT); | |
434 | break; | |
435 | } | |
436 | ||
437 | // Clear Screen | |
438 | rc.clear(); | |
439 | cph.clearScreen(); | |
440 | ||
441 | } while (key != VK_ESCAPE); | |
442 | ||
443 | // Reset console | |
444 | cph.reset(); | |
445 | ||
446 | // If we aren't on windows, we can't animate and have | |
447 | // to write OS specific code to handle key presses | |
448 | #else | |
449 | ||
450 | // Variable to hold key presses | |
451 | char key; | |
452 | ||
453 | // Play Game | |
454 | do { | |
455 | // Draw the board to the render object | |
456 | gb.draw(rc); | |
457 | ||
458 | // Draw the render object | |
459 | rc.render(); | |
460 | ||
461 | // Get Key Press | |
462 | key = getchar(); | |
463 | ||
464 | } while (key != 0); | |
465 | ||
466 | #endif | |
467 | } | |
468 | ||
469 | // EoF |