| 1 | /* hPong 0.3 |
| 2 | * <~~Harvie 2oo8 |
| 3 | * Released under Creative Commons |
| 4 | * |
| 5 | * Requirements: |
| 6 | * - stty |
| 7 | * - ansi terminal with resolution >= 80x24 characters |
| 8 | */ |
| 9 | |
| 10 | #include <stdio.h> |
| 11 | #include <stdlib.h> |
| 12 | #include <signal.h> |
| 13 | |
| 14 | //Settings |
| 15 | char pixchar = '#'; |
| 16 | |
| 17 | int turntime = 25; //Time of turn in ms (inverted speed) |
| 18 | int debugturntime = 0; //Turntime for debuging |
| 19 | |
| 20 | unsigned char padsize = 1; //Size of pad |
| 21 | char padspeed = 2; //Speed of pad (pixels per keypress) |
| 22 | |
| 23 | unsigned char ui_skill_l = 255; //Skill of UI = (0-255); 0=disabled, 255=godmode |
| 24 | unsigned char ui_skill_r = 200; //Skill of UI = (0-255); 0=disabled, 255=godmode |
| 25 | unsigned char debug = 0; //UI Debug mode |
| 26 | |
| 27 | unsigned char music = 0; //Enable music? (Bool) |
| 28 | char music_cmd[] ="while true; do mplayer /usr/share/chiptunes/* >/dev/null 2>&1; done;"; |
| 29 | |
| 30 | unsigned char arduino = 0; //Enable Arduino? (Bool) |
| 31 | char arduinodev[] = "/dev/ttyUSB0"; |
| 32 | |
| 33 | //Stadium |
| 34 | unsigned char *stadium_bitmap[] = { |
| 35 | "#### hPong 0.3 ################################################", |
| 36 | "# | #", |
| 37 | "# #", |
| 38 | "# | #", |
| 39 | "# #", |
| 40 | "# | #", |
| 41 | "# #", |
| 42 | "# | #", |
| 43 | "# #", |
| 44 | "# | #", |
| 45 | "# #", |
| 46 | "# | #", |
| 47 | "# #", |
| 48 | "# | #", |
| 49 | "# #", |
| 50 | "# | #", |
| 51 | "########################################### <~~Harvie 2oo8 ####"}; |
| 52 | char stadium_lines = 17; //15+2 |
| 53 | char stadium_width = 63; //60+2+1 |
| 54 | |
| 55 | //Banners |
| 56 | unsigned char banner_start[] = |
| 57 | "\n\n\n\n\n\n\n\n\n\n\n" |
| 58 | " ## #### by: <~~Harvie 2oo8\n" |
| 59 | " ## # #\n" |
| 60 | " ## ## ## #### ### #### ###\n" |
| 61 | " ####### ###### # # # # # # #\n" |
| 62 | " ## ## ## # ### # # ####\n" |
| 63 | " ## ## ## #\n" |
| 64 | " ## Press any key to start! ###\n"; |
| 65 | |
| 66 | unsigned char banner_lose[] = |
| 67 | "\n\n\n\n\n\n\n\n\n\n\n\n\n" |
| 68 | " ### # # ### # # # ### #\n" |
| 69 | " # # # # # # # # # #\n" |
| 70 | " # # ### ### ### # # #\n" |
| 71 | " # # # # # # # # #\n" |
| 72 | " ### # # ### # # # # #\n" |
| 73 | "\n" |
| 74 | " Score: %d : %d\n" |
| 75 | " (Press Any Key to continue...)\n"; |
| 76 | |
| 77 | //Numbers |
| 78 | char stadium_offset = 7; |
| 79 | char score_space[] = " "; |
| 80 | unsigned char *num_bitmaps[]={ |
| 81 | "#### ## ## ####", //0 |
| 82 | " # # # # #", //1... |
| 83 | "### ##### ###", |
| 84 | "### #### ####", |
| 85 | " # # ### # ", |
| 86 | "#### ### ####", |
| 87 | "#### #### ####", |
| 88 | "### # # # # ", |
| 89 | "#### ##### ####", |
| 90 | "#### #### ####"}; //9 |
| 91 | |
| 92 | //Other Values |
| 93 | char stadium_max; //Set by init() |
| 94 | |
| 95 | char ball_c = 1; //x |
| 96 | char ball_l = 0; //y |
| 97 | char ballmove_c = 1; //x |
| 98 | char ballmove_l = 1; //y |
| 99 | |
| 100 | char player_l = 7; //Left |
| 101 | char player_r = 7; //Right |
| 102 | |
| 103 | unsigned int score_l = 0; |
| 104 | unsigned int score_r = 0; |
| 105 | |
| 106 | //Misc |
| 107 | char loop=0; |
| 108 | char temp; |
| 109 | FILE *musicfd = NULL; |
| 110 | FILE *arduinofd = NULL; |
| 111 | |
| 112 | //Functions |
| 113 | void blank() { |
| 114 | printf("\033[2J"); //Clear screen |
| 115 | printf("\033[0;0H"); //L:C = 0:0 |
| 116 | } |
| 117 | |
| 118 | void quit() { |
| 119 | if(arduino) fclose(arduinofd); |
| 120 | ualarm(0, 0); |
| 121 | system("stty icanon"); |
| 122 | blank(); |
| 123 | printf("hPong exited! Scrore was %d : %d\n<~~Harvie 2oo8\n", score_l, score_r); |
| 124 | exit(255); |
| 125 | if(music) pclose(musicfd); |
| 126 | } |
| 127 | |
| 128 | void music_start() { |
| 129 | if( (musicfd = popen(music_cmd, "r"))==NULL ) { |
| 130 | printf("Cannot initialize music\n"); |
| 131 | quit(); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | void win(char player) { |
| 136 | switch(player) { |
| 137 | case 'l': |
| 138 | score_l++; |
| 139 | ball_l = 2; ball_c = stadium_width-3; ballmove_c = -1; |
| 140 | break; |
| 141 | |
| 142 | case 'r': |
| 143 | score_r++; |
| 144 | ball_l = 2; ball_c = 3; ballmove_c = 1; |
| 145 | break; |
| 146 | } |
| 147 | blank(); |
| 148 | printf(banner_lose, score_l, score_r); |
| 149 | if(!debug) sleep(1); |
| 150 | if(!arduino && (ui_skill_l == 0 || ui_skill_r == 0)) getchar(); |
| 151 | blank(); |
| 152 | } |
| 153 | |
| 154 | void reset_scr() { |
| 155 | printf("\033[0;0H"); //L:C = 0:0 |
| 156 | } |
| 157 | |
| 158 | void pixel(char c, char line, char col) { |
| 159 | printf("\033[%d;%dH", line, col); |
| 160 | putchar(c); |
| 161 | printf("\033[0;0H"); //L:C = 0:0 |
| 162 | } |
| 163 | |
| 164 | void draw_stadium() { |
| 165 | unsigned char i, x, sl, sr; |
| 166 | sl = score_l%10; sr = score_r%10; |
| 167 | for(i=0;i<stadium_offset;i++) { |
| 168 | printf("\033[K"); |
| 169 | if(i>=1 && i<=5) { |
| 170 | x = (i-1)*3; |
| 171 | printf("%s%c%c%c . %c%c%c", score_space, |
| 172 | num_bitmaps[sl][x], num_bitmaps[sl][x+1], num_bitmaps[sl][x+2], |
| 173 | num_bitmaps[sr][x], num_bitmaps[sr][x+1], num_bitmaps[sr][x+2] |
| 174 | ); |
| 175 | } |
| 176 | putchar('\n'); |
| 177 | } |
| 178 | for(i=0;i<stadium_lines;i++) printf("%s\033[K\n", stadium_bitmap[i]); |
| 179 | printf("\033[K"); |
| 180 | } |
| 181 | |
| 182 | void recalc() { |
| 183 | //UI - follow ball |
| 184 | if((unsigned char)rand() < ui_skill_l) { //Left |
| 185 | if(player_l < ball_l) player_l++; |
| 186 | if(player_l > ball_l) player_l--; |
| 187 | } |
| 188 | |
| 189 | if((unsigned char)rand() < ui_skill_r) { //Right |
| 190 | if(player_r < ball_l) player_r++; |
| 191 | if(player_r > ball_l) player_r--; |
| 192 | } |
| 193 | |
| 194 | //Move ball |
| 195 | ball_c += ballmove_c; |
| 196 | ball_l += ballmove_l; |
| 197 | |
| 198 | //Pong (horizontal) |
| 199 | if(ball_c == 1) { //left |
| 200 | if(abs(ball_l-player_l) <= padsize+1) { |
| 201 | ballmove_c = -ballmove_c; |
| 202 | ball_c++; |
| 203 | } else { |
| 204 | win('r'); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | if(ball_c == stadium_width-3) { //right |
| 209 | if(abs(ball_l-player_r) <= padsize+1) { |
| 210 | ballmove_c = -ballmove_c; |
| 211 | ball_c--; |
| 212 | } else { |
| 213 | win('l'); |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | //Bounce ball (vertical) |
| 218 | if(ball_l >= stadium_max || ball_l <= 0) ballmove_l = -ballmove_l; |
| 219 | } |
| 220 | |
| 221 | void redraw() { |
| 222 | char i; |
| 223 | //Clear |
| 224 | reset_scr(); |
| 225 | |
| 226 | //Stadium |
| 227 | draw_stadium(); |
| 228 | |
| 229 | //Check player pos limits |
| 230 | if(player_r < 0) player_r = 0; |
| 231 | if(player_r > stadium_max) player_r = stadium_max; |
| 232 | |
| 233 | if(player_l < 0) player_l = 0; |
| 234 | if(player_l > stadium_max) player_l = stadium_max; |
| 235 | |
| 236 | //Pads |
| 237 | for(i=-padsize;i<padsize+1;i++) pixel(pixchar, stadium_offset+2+i+player_l, 2); |
| 238 | for(i=-padsize;i<padsize+1;i++) pixel(pixchar, stadium_offset+2+i+player_r, stadium_width-1); |
| 239 | |
| 240 | //Ball |
| 241 | pixel(pixchar, stadium_offset+2+ball_l, 2+ball_c); |
| 242 | fflush(stdout); |
| 243 | } |
| 244 | |
| 245 | void splash() { |
| 246 | blank(); |
| 247 | puts(banner_start); |
| 248 | getchar(); |
| 249 | blank(); |
| 250 | } |
| 251 | |
| 252 | void control(unsigned char key) { |
| 253 | switch(key) { |
| 254 | case 'A': player_r-=padspeed; break; //A = Arrow UP |
| 255 | case 'B': player_r+=padspeed; break; //B = Arrow DOWN |
| 256 | case 'q': case 'Q': |
| 257 | loop=0; |
| 258 | quit(); |
| 259 | break; |
| 260 | case 'e': case 'E': player_l-=padspeed; break; //e key |
| 261 | case 'd': case 'D': player_l+=padspeed; break; //D key |
| 262 | } |
| 263 | |
| 264 | redraw(); |
| 265 | } |
| 266 | |
| 267 | void alarm_handle(int signo) { |
| 268 | recalc(); |
| 269 | redraw(); |
| 270 | if(signo == SIGTERM || signo == SIGINT || signo == SIGQUIT) quit(); |
| 271 | } |
| 272 | |
| 273 | void init() { |
| 274 | //Blank screen |
| 275 | blank(); |
| 276 | //Init random generator |
| 277 | srand(time(NULL)); |
| 278 | //Disable both input buffers |
| 279 | setbuf(stdin, NULL); |
| 280 | system("stty -icanon"); |
| 281 | //Handle alarm (timer) |
| 282 | signal(SIGALRM, alarm_handle); |
| 283 | signal(SIGINT, alarm_handle); signal(SIGTERM, alarm_handle); signal(SIGQUIT, alarm_handle); |
| 284 | //Compute lower sprite position in stadium |
| 285 | stadium_max = stadium_lines - 3; |
| 286 | //Debug mode |
| 287 | if(debug) turntime = debugturntime; |
| 288 | //Open Arduino |
| 289 | if( arduino&&((arduinofd=fopen(arduinodev,"rb"))==NULL) ) { |
| 290 | printf("Cannot open %s\n", arduinodev); |
| 291 | exit(255); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | //Main... Short & simple ;) |
| 296 | int main() { |
| 297 | init(); |
| 298 | splash(); |
| 299 | |
| 300 | //Music |
| 301 | if(music) music_start(); |
| 302 | |
| 303 | loop = 1; |
| 304 | ualarm(turntime*1000+1, turntime*1000+1); |
| 305 | while(loop) { |
| 306 | if(arduino) player_r = getc(arduinofd); //Arduino |
| 307 | else control(getchar()); //Keyboard |
| 308 | } |
| 309 | quit(); |
| 310 | } |
| 311 | |