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