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: