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