001: #include <stdio.h>
002: #include <stdlib.h>
003: #include <unistd.h>
004: #include <time.h>
005: 
006: // constants that can be used in the code
007: #define COLS 97
008: #define ROWS 23
009: #define CA   '+'
010: #define CH   '-'
011: #define CV   '|'
012: 
013: struct ball
014: {
015:   int r, c;       // row and col coordinates
016:   int h, v;       // encode the horizontal and vertical components of movement
017: };
018: 
019: // ALREADY DEFINED FUNCTIONS:
020: 
021: void gotoxy(int, int);                   // move the cursor to a given screen position (column and row, respectively)
022:                                          // when used before a printf() or similar functions it allows to print at an arbitrary
023:                                          // position on the screen
024:                                          // the top-left position on the screen has (0, 0) coordinates.
025:                                          //  column index increases moving right
026:                                          //  row index increases moving down
027: 
028: char getcommand(void);                   // read a char from keyboard in a non blocking fashion, returns 0 if no key has been stroke
029: 
030: 
031: void clearscreen(void);                  // clear the whole screen (actually, not needed)
032: 
033: // PROTOTYPES OF FUNCTIONS TO BE DEFINED (ADAPT THEM ACCORDING TO YOUR DATA STRUCTURES)
034: void printfield(void);
035: void printball(struct ball);             // print the ball on the screen
036: void updaball(struct ball *, char key);  // update ball position
037: void eraseball(struct ball);             // erase ball
038: 
039: int main()
040: {
041:   struct ball myball;     // the ball that is bouncing...
042:   char key;               // every cycle I try to read if user pushed a key
043:   int pause = 200000;     // initial delay
044: 
045:   // randomly init the ball
046:   srand(time(NULL));
047:   myball.c = rand() % COLS;
048:   myball.r = rand() % ROWS;     // initial position must be inside the field
049:   myball.h = rand()%2 ? 1 : -1; // can assume -1 or 1 values only
050:   myball.v = rand()%2 ? 1 : -1; // can assume -1 or 1 values only
051: 
052:   printfield();
053:   do
054:   {
055:     printball(myball);       // print the ball
056:     gotoxy(COLS+3, ROWS+2);  // just put the writing position outside the field (not requested, but the output is a bit more appealing)
057:     //fflush(stdout); // uncomment for linux...
058:     usleep(pause);           // wait a bit
059:     key = getcommand();      // check whether the user pressed a key 
060:     eraseball(myball);       // delete the ball
061:     updaball(&myball, key);  // update the position also taking in account the pressed key
062:   }
063:   while(key != 'x');         // repeat until someone pressed 'x'
064: 
065: 
066:   return 0;
067: }
068: 
069: void printball(struct ball a)
070: {
071:   gotoxy(a.c, a.r);   // simply move the writing position at ball position
072:   printf("O");        // write the ball
073: }
074: 
075: void eraseball(struct ball a)
076: {
077:   gotoxy(a.c, a.r);
078:   printf(" ");
079: } // same as the previous function, but write a space to delete the ball
080: 
081: void updaball(struct ball *a, char k)
082: {
083:   // do I have to modify the direction?
084:   switch(k)
085:   {
086:     case 'q':
087:       {
088:   int tmp = a->v;
089:   a->v = -a->h;
090:   a->h = tmp;
091:       } // clockwise rotation
092:       break;
093:     case 'e':
094:       {
095:   int tmp = a->v;
096:   a->v = a->h;
097:   a->h = -tmp;
098:       } // counterclockwise rotation
099:       break;
100:   }
101: 
102:   // update ball position
103:   a->c = a->c + a->h;
104:   a->r = a->r + a->v;
105: 
106:   // we need to check whether the ball reached a field border, this does not modify position 
107:   // but future movements only
108:   if(a->c == COLS + 1) // nearby right border, when true we have to bounce (namely inverting horizontal movement)
109:     a->h = -1;
110:   if(a->c == 1)        // nearby left  border, when true we have to bounce (namely inverting horizontal movement)
111:     a->h = 1;
112:   if(a->r == ROWS + 1) // nearby bottom border, when true we have to bounce (namely inverting vertical movement)
113:     a->v = -1;
114:   if(a->r == 1)        // nearby top  border, when true we have to bounce (namely inverting vertical movement)
115:     a->v = 1;
116: }
117: 
118: 
119: void printfield(void)
120: {
121:   clearscreen();
122:   // print upper border
123:   putchar(CA); // same as printf("%c", CA);
124:   for(int i = 0; i <= COLS; ++i) // <= because we want COLS "free" space
125:     putchar(CH); // same as printf("%c", CH);
126:   putchar(CA); // same as printf("%c", CA);
127: 
128:   // print vertical borders exploing the gotoxy() function
129:   int screenrow = 1;
130:   for(int i = 0; i <= ROWS; ++i)
131:   {
132:     gotoxy(0, screenrow);
133:     putchar(CV); // same as printf("%c", CV);
134:     gotoxy(COLS + 2, screenrow);
135:     putchar(CV); // same as printf("%c", CV);
136:     ++screenrow;
137:   }
138: 
139:   // print bottom border 
140:   gotoxy(0,screenrow);
141:   putchar(CA); // same as printf("%c", CA);
142:   for(int i = 0; i <= COLS; ++i) // <= because we want COLS "free" space
143:     putchar(CH); // same as printf("%c", CH);
144:   putchar(CA); // same as printf("%c", CA);
145: 
146:   //fflush(stdout); // needed on linux... do not worry about this
147: }
148: 
149: 
150: 
151: 
152: 
153: 
154: 
155: 
156: 
157: 
158: 
159: 
160: 
161: 
162: 
163: 
164: 
165: 
166: 
167: 
168: 
169: 
170: 
171: 
172: 
173: 
174: 
175: 
176: 
177: 
178: 
179: 
180: 
181: 
182: 
183: 
184: 
185: 
186: // PREDEFINED FUNCTIONS DO NOT MODIFY BELOW THIS LINE!
187: 
188: #ifdef _WIN32
189: #define WIN32_LEAN_AND_MEAN
190: #include <windows.h>
191: #include <conio.h>
192: 
193: char getcommand(void)
194: {
195: 
196:   char ch = 0;
197: 
198:   if (kbhit())
199:   {
200:     ch = getch(); // Read the character
201:   }
202: 
203:   return ch;
204: }
205: 
206: void gotoxy(int x, int y)
207: {
208:   COORD coord = {.X = x, .Y = y};
209:   SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
210: }
211: 
212: void clearscreen()
213: {
214:   HANDLE                     hStdOut;
215:   CONSOLE_SCREEN_BUFFER_INFO csbi;
216:   DWORD                      count;
217:   DWORD                      cellCount;
218:   COORD                      homeCoords = { 0, 0 };
219: 
220:   hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
221:   if (hStdOut == INVALID_HANDLE_VALUE) return;
222: 
223:   /* Get the number of cells in the current buffer */
224:   if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return;
225:   cellCount = csbi.dwSize.X *csbi.dwSize.Y;
226: 
227:   /* Fill the entire buffer with spaces */
228:   if (!FillConsoleOutputCharacter(
229:     hStdOut,
230:     (TCHAR) ' ',
231:     cellCount,
232:     homeCoords,
233:     &count
234:     )) return;
235: 
236:   /* Fill the entire buffer with the current colors and attributes */
237:   if (!FillConsoleOutputAttribute(
238:     hStdOut,
239:     csbi.wAttributes,
240:     cellCount,
241:     homeCoords,
242:     &count
243:     )) return;
244: 
245:   /* Move the cursor home */
246:   SetConsoleCursorPosition( hStdOut, homeCoords );
247: }
248: 
249: #else // !_WIN32
250: #include <unistd.h>
251: #include <term.h>
252: 
253: 
254: void gotoxy(int x, int y)
255: {
256:   printf("%s%d;%d%c", "\033[", y+1, x+1, 'H');
257:   fflush(stdout);
258: }
259: 
260: void clearscreen()
261: {
262:    printf("\e[2J\e[H");
263: }
264: 
265: char getcommand(void)
266: {
267:   gotoxy(COLS, ROWS);
268:   printf("\b \b");
269:   struct termios orig_term, raw_term;
270: 
271:   // Get terminal settings and save a copy for later
272:   tcgetattr(STDIN_FILENO, &orig_term);
273:   raw_term = orig_term;
274: 
275:   // Turn off echoing and canonical mode
276:   raw_term.c_lflag &= ~(ECHO | ICANON);
277: 
278:   // Set min character limit and timeout to 0 so read() returns immediately
279:   // whether there is a character available or not
280:   raw_term.c_cc[VMIN] = 0;
281:   raw_term.c_cc[VTIME] = 0;
282: 
283:   // Apply new terminal settings
284:   tcsetattr(STDIN_FILENO, TCSANOW, &raw_term);
285: 
286:   char ch = 0;
287:   int len = read(STDIN_FILENO, &ch, 1);
288:   if(len == 1)
289:   {
290:     printf("\b \b");
291:     fflush(stdout);
292:   }
293: 
294:   // Make sure no characters are left in the input stream as
295:   // plenty of keys emit ESC sequences, otherwise they'll appear
296:   // on the command-line after we exit.
297:   while(read(STDIN_FILENO, &ch, 1)==1)
298:     printf("\b \b");;
299: 
300:   // Restore original terminal settings
301:   tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
302:   if(len == 1)
303:     printf("\b \b");;
304:   return ch;
305: }
306: #endif
307: