View difference between Paste ID: jGAgEdXj and rR2h8DTk
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