/* ********************************************** * * * * * BLACKBOX * * * * * * A thinking man's game from the * * * "Mathematical Games" column of * * * THE SCIENTIFIC AMERICAN Magazine * * * * * * * * * Written for the IBM-PC by Joe Long * * * Rt. 1 Box 100, Madison, AL 35758 * * * V 1.0 October, 1983 * * * * * * Program donated to the PUBLIC DOMAIN * * * * * * Transcribed to C from GWBASIC * * * by Warren Porter * * * * * * This program may be freely distributed * * * but I ask that the source and .exe * * * files be distributed together. * * * This was compiled using Microsoft * * * Quick C. User assumes all risks. * * * * * ********************************************** */ #include #include #include #define LO 3 #define HI 14 #define RED 12 #define GREEN 10 #define RETURN 13 #define ESCAPE 27 #define F1 59 #define F2 60 #define F3 61 #define F4 62 #define F5 63 #define F10 68 #define LEFT 75 #define RIGHT 77 #define UP 72 #define DOWN 80 unsigned randinit(); void cls(); void stall(int delay); void write_scr(int col, int row, unsigned char attrib, char *text); void place_cursor(int row, int col); void print_ports(unsigned char plabel[33][3]); void draw_gun(int col, int row, int work); void do_instruction(); void draw_screen(); void sample_game(); void set_cursor(int hm, int lm); unsigned char str[] = "ΙΝΝΝΡΝΝΝΡΝΝΝΡΝΝΝΡΝΝΝΡΝΝΝΡΝΝΝΡΝΝΝ»", ss[] = "Ί ³ ³ ³ ³ ³ ³ ³ Ί", sl[] = "ΗΔΔΔΕΔΔΔΕΔΔΔΕΔΔΔΕΔΔΔΕΔΔΔΕΔΔΔΕΔΔΔΆ", sbr[] = "ΘΝΝΝΟΝΝΝΟΝΝΝΟΝΝΝΟΝΝΝΟΝΝΝΟΝΝΝΟΝΝΝΌ"; void main() { int x, y, i, j, ball[10][10], guess[10][10], dir, working=1, exit_point, entry, row, col, mode, omode, labelnumber, nscore, crow, ccol, numballs, guessnumber, sball, trow, tcol; unsigned char color, buff[79], plabel[33][3], ans, pointer[3], rlab[3]=" ", exit_box, cursor; float rhold; srand(randinit()); cls(); /* ** Initialize screen graphics strings ** */ /* ** Opening Questions ** */ color = 15; set_cursor(16,0); write_scr(30,1,color,"B L A C K B O X"); color = 7; write_scr(20, 3,color,"Written for the IBM-PC by Joe Long"); write_scr(20, 5,RED, "Transcribed to C by Warren Porter"); write_scr(1,7,LO,"Do you need instructions? "); set_cursor(0,16); place_cursor(30,7); ans = getch(); if ((ans == 'y') || (ans == 'Y')) do_instruction(); while (working == 1) { /* Main processing loop */ cls(); color = LO; draw_screen(); /* ********************** */ /* ** Main Program ** */ /* ********************** */ /* ** Initialize Values ** */ for (i=1; i < 33; i++) memcpy(plabel[i]," ",3); labelnumber = ccol = 1 , nscore = crow = numballs = mode = omode = 0; for (i=0;i<10;i++) for (j=0;j<10;j++) ball[i][j] = guess[i][j] = 0; draw_screen(); write_scr(70, 7, color, "Number"); write_scr(72, 9, color, "of"); write_scr(70, 11, color, "Balls:"); do { set_cursor(0,16); place_cursor(72, 13); buff[0]=getch(); if (buff[0] >= '0' && buff[0] <= '9') numballs = buff[0] - '0'; else putch(7); } while (numballs < 1); set_cursor(-1,0); for (i=0; i < numballs;i++) { rhold= (float)rand()/RAND_MAX; x = rhold * 8 + 1; rhold= (float)rand()/RAND_MAX; y = rhold * 8 + 1; if (ball[x][y]) i--; else ball[x][y] = 1; } buff[1]= 0; write_scr(72,13,RED,buff); /* ** Main Menu ** */ write_scr(7, 5, LO, "Menu"); write_scr(1,7, LO, "------------------"); write_scr(1,8, LO, "Launch Ray F1"); write_scr(1,10, LO, "Place Ball F2"); write_scr(1,12, LO, "Remove Ball F3"); write_scr(1,14, LO, "Clear Box F4"); write_scr(1,16, LO, "Score Box F5"); write_scr(1,18, LO, "Quit Game F10 "); write_scr(1,20, LO, "Back Out esc"); write_scr(1,21, LO, "------------------"); while (working == 1) { ans=getch(); switch (ans) { case RETURN: { switch (mode) { case 1 : { /* Fire ray */ /* ** Evaluate Ray ** */ row = crow, col = ccol; /* save cursor location */ if (row == 0) dir = 1; if (col == 9) dir = 2; if (row == 9) dir = 3; if (col == 0) dir = 4; switch (dir) { case 1: { entry = col; break;} case 2: { entry = 8 +row; break;} case 3: { entry = 25-col; break;} case 4: { entry = 33 - row;} } exit_box = ' '; /* Check for immediate hit or reflection */ if (((dir == 1) && (ball[row +1][col])) || ((dir == 2) && (ball[row][col-1])) || ((dir == 3) && (ball[row-1][col])) || ((dir == 4) && (ball[row][col +1]))) exit_box = 'H'; else if (((dir == 1) && ((ball[1][col-1]) || (ball[1][col+1]))) || ((dir == 2) && ((ball[row-1][8]) || (ball[row+1][8]))) || ((dir == 3) && ((ball[8][col-1]) || (ball[8][col+1]))) || ((dir == 4) && ((ball[row-1][1]) || (ball[row+1][1])))) exit_box = 'R'; switch (dir) { /* Make first move into box */ case 1 : {row++; break;} case 2 : {col--; break;} case 3 : {row--; break;} case 4 : {col++; break;} } /* Check for hit */ while (exit_box == ' ' && row > 0 && row < 9 && col > 0 && col < 9) { if (((dir == 1) && (ball[row +1][col])) || ((dir == 2) && (ball[row][col-1])) || ((dir == 3) && (ball[row-1][col])) || ((dir == 4) && (ball[row][col +1]))) { exit_box = 'H'; break; } if (((dir == 1) && ((ball[row+1][col-1]) && (ball[row+1][col+1]))) || ((dir == 2) && ((ball[row-1][col-1]) && (ball[row+1][col-1]))) || ((dir == 3) && ((ball[row-1][col-1]) && (ball[row-1][col+1]))) || ((dir == 4) && ((ball[row-1][col+1]) && (ball[row+1][col+1])))) { exit_box = 'R'; break;} /* Check for change of direction */ switch (dir) { case 1: { if (ball[row + 1][col - 1]) dir = 4; if (ball[row + 1][col + 1]) dir = 2; break; } case 2: { if (ball[row-1][col-1]) dir = 1; if (ball[row +1][col-1]) dir = 3; break; } case 3: { if (ball[row-1][col-1]) dir = 4; if (ball[row-1][col +1]) dir = 2; break; } case 4: { if (ball[row-1][col+1]) dir = 1; if (ball[row +1][col+1]) dir = 3; break; } } /* Move Ray */ switch (dir) { case 1 : {row++; break;} case 2 : {col--; break;} case 3 : {row--; break;} case 4 : {col++; break;} } } /* End still inside of box */ exit_point = 0; if (exit_box == ' ') { if (row == 9) exit_point = 25 - col; if (col == 0) exit_point = 33 - row; if (row == 0) exit_point = col; if (col == 9) exit_point = row + 8; if (!exit_point) { fprintf(stderr,"Inproper exit from inside box\a"); exit(1); } } /* Change labels in array */ if (exit_box == ' ') { nscore += 2; rlab[1] = labelnumber + 64; labelnumber++; if (labelnumber == 8) labelnumber++; strcpy(plabel[exit_point], rlab); } else { nscore++; rlab[1] = exit_box; } strcpy(plabel[entry], rlab); /* Print new port labels & return to menu */ print_ports(plabel); mode= 0; /* Reset the mode */ draw_gun(ccol,crow,0); set_cursor(-1,-1); sprintf(buff,"%2d",nscore); write_scr(71,19,RED,buff); break; } case 2: { /* Return on placing ball */ /* ******************************* */ /* ** Place balls in square ** */ /* ******************************* */ if (guess[trow][tcol]) { putch(7); /* Beep */ break; } guess[trow][ tcol] = 1; guessnumber++; sball = 2; buff[0] = sball; buff[1] = 0; write_scr(26 + 4*tcol, 4 + 2 * trow,HI,buff); mode = 0; break; } /* ********************************** */ /* ** Remove Ball from square ** */ /* ********************************** */ case 3: { /* Remove a ball */ if (guess[trow][tcol] == 0) { putch(7); /* Beep */ break; } guess[trow][tcol] = 0; guessnumber--; sball = ' '; buff[0] = sball; buff[1] = 0; write_scr(26 + 4*tcol, 4 + 2 * trow,HI,buff); mode = 0; break; } } /* End switch for RETURN */ set_cursor(-1,0); break; } case ESCAPE : { draw_gun(ccol,crow,0); set_cursor(-1,-1); if (mode == 0) { working=0; break; } mode = 0; break; } /* End case ESCAPE */ case 0 : { /* A function or arrow was pressed */ cursor = getch(); switch (cursor) { case F1 : { if (mode > 1) set_cursor(-1,-1); draw_gun(ccol,crow,1); mode = 1; break; } case F2 : { if (mode == 1) { draw_gun(ccol,crow,0); } row = trow = col = tcol = 5; set_cursor(64,16); place_cursor(26 + 4 * tcol, 4 + 2 * trow); mode = 2; break; } case F3 : { if (mode == 1) { draw_gun(ccol,crow,0); } row = trow = col = tcol = 5; set_cursor(64,16); place_cursor(26 + 4 * tcol, 4 + 2 * trow); mode = 3; break; } case F4 : { /* Clear all balls */ sball = ' '; buff[0] = sball; buff[1] = 0; for (row = 1;row < 9;row++) for (col = 1;col < 9;col++) { guess[row][ col] = 0; write_scr(26 + 4*col, 4 + 2 * row,HI,buff); } guessnumber = mode = 0; draw_gun(ccol,crow,0); set_cursor(-1,-1); break; } case F5: { /* Score box */ if (mode == 1) draw_gun(ccol,crow,0); if (mode > 1) set_cursor(-1,-1); buff[0] = sball; buff[1] = 0; trow = row ; tcol = col; /* save row & col */ for (row = 1;row < 9;row++) for (col = 1;col < 9;col++) { if (guess[row][col] == ball[row][col]) { color = GREEN; buff[0] = (ball[row][col]) ? 2 : ' '; } else { nscore += 5; color = RED; buff[0] = (ball[row][col]) ? 2 : 1; } write_scr(26 + 4*col, 4 + 2 * row,color,buff); } sprintf(buff,"%2d",nscore); write_scr(71,19,RED,buff); working = 0; break; } case F10 : { /* Emergency quit */ cls(); working = -1; break; } case UP: case DOWN: case RIGHT: case LEFT: { if (mode < 1 ) mode = 1; if (mode == 1) { draw_gun(ccol,crow,0); if (cursor == RIGHT && ccol < 8) { ccol++; crow = (crow < 5)? 0 : 9; } if (cursor == LEFT && ccol > 1) { ccol--; crow = (crow < 5)? 0 : 9; } if (cursor == UP && crow > 1) { crow--; ccol = (ccol < 5)? 0 : 9; } if (cursor == DOWN && crow < 8) { crow++; ccol = (ccol < 5)? 0 : 9; } draw_gun(ccol,crow,1); } if (mode > 1) { /* Setting new cursor won't require clearing old */ if (cursor == RIGHT && tcol < 8) tcol++; if (cursor == LEFT && tcol > 1) tcol--; if (cursor == UP && trow > 1) trow--; if (cursor == DOWN && trow < 8) trow++; place_cursor(26 + 4 * tcol, 4 + 2 * trow); } break; } } } /* End of case zero (top of a function or arrow key */ // default: say(ans); /* beep */ } } if (working == -1) { set_cursor(0,0); cls(); place_cursor(1,1); break; } write_scr(34,24 , HI,"Another Game? n/Y"); ans = getch(); set_cursor(0,0); if (ans == 'n' || ans == 'N') { working = 0; cls(); place_cursor(1,1); printf("Y'all come back now.\n"); stall(1); } else working = 1; } set_cursor(9478,7); } void draw_screen() { /* ********************************** */ /* ** Screen graphics routines ** */ /* ********************************** */ /* ** Print field ** */ cls(); write_scr(40,1,LO, "BLACKBOX"); write_scr(28, 5, LO, str); write_scr(28, 6, LO, ss); write_scr(28, 7, LO, sl); write_scr(28, 8, LO, ss); write_scr(28, 9, LO, sl); write_scr(28,10, LO, ss); write_scr(28,11, LO, sl); write_scr(28,12, LO, ss); write_scr(28,13, LO, sl); write_scr(28,14, LO, ss); write_scr(28,15, LO, sl); write_scr(28,16, LO, ss); write_scr(28,17, LO, sl); write_scr(28,18, LO, ss); write_scr(28,19, LO, sl); write_scr(28,20, LO, ss); write_scr(28,21, LO, sbr); write_scr( 70, 17, RED, "SCORE"); } void sample_game() { /* ****************************************** */ /* ** Animated sample for instructions ** */ /* ****************************************** */ static unsigned char sball[2] = "\x02"; cls(); draw_screen(); write_scr(30,10,HI,sball); write_scr(46,12,HI,sball); write_scr(46,16,HI,sball); /* ** Demonstrate Rays ** */ stall(1); write_scr(24,10,RED,"-\x1a"); stall(1); write_scr(24,10,LO," H"); write_scr(71,19,RED, " 1"); stall(1); write_scr(24,8,RED,"-\x1a"); stall(1); write_scr(24,8,LO," R"); write_scr(71,19,RED, " 2"); stall(1); write_scr(46,3,RED,"\x19"); stall(1); write_scr(46,3,LO," "); write_scr(46,4,LO,"H"); write_scr(71,19,RED, " 3"); stall(1); write_scr(42,3,RED,"\x19"); stall(1); write_scr(42,3,LO," "); write_scr(42,4,LO,"H"); write_scr(71,19,RED, " 4"); stall(1); write_scr(63,12,RED,"\x1b-"); stall(1); write_scr(62,12,LO,"H "); write_scr(71,19,RED, " 5"); stall(1); write_scr(63, 14, RED, "\x1b-"); stall(1); write_scr(62, 14, LO, "R "); write_scr(71, 19, RED ," 6"); stall(1); write_scr(63, 10, RED, "\x1b-"); stall(1); write_scr(62, 10, LO , "A "); stall(1); write_scr(50, 4, LO, "A"); write_scr(71, 19, RED, " 8"); stall(1); write_scr(42, 23, RED, "\x18"); stall(1); write_scr(42, 23, LO, " "); write_scr(42, 22, LO, "B"); stall(1); write_scr(26, 18, LO, "B"); write_scr(71, 19, RED, "10"); write_scr(28, 24, LO, ""); getch(); cls(); } void write_scr(int col, int row, unsigned char attrib, char *text) { char far *ptr; ptr = (char far *) 0xb8000000L + (row - 1) * 160 + ((col- 1) << 1); while (*text) { *(ptr++) = *(text++); *(ptr++) = attrib; } } void cls() { unsigned char far *ptr; int i; ptr = (unsigned char far *) 0xb8000000L; for (i=0;i < 2000;i++) { *(ptr++) = ' '; *(ptr++) = 7; } } void place_cursor(int col, int row) { row--, col--; _asm { mov dh,row mov dl,col mov bh,0 ; Page 0 mov ah,2 ; Service 2, 10h int 10h ; Set Cursor position } } unsigned randinit() { _asm { mov ax, 0 ; prep to read time int 1ah ; read time of day mov cx, 16 ; set loop counter top: rcl dx,1 ; shift leading bit into carry rcr ax,1 ; shift carry into ax loop top ; do it 16 times } } void do_instruction() { int i,j; static char cont_message[] = ""; static char *message[] = { " ", /* For printing control M*/ " The game of `BLACKBOX' is taken from the `Mathematical Games' column of `THE", "SCIENTIFIC AMERICAN' magazine. It is a logic puzzle, with the computer serving", "only as draftsman and scorekeeper -- you must do all the thinking!", " ", " The game is played on an 8x8 grid, representing 64 positions in a closed", "`black box.' The box contains some balls, located at random on the grid. You", "try to deduce the location of the balls from the behavior of `rays' that you", "shoot in from the edges of the box.", " ", " The behavior of the rays follows these rules: If a ray strikes a ball head-", "on, it is absorbed. If it would pass by a ball on an adjacent square, it turns", "ninety degrees so as to avoid the ball, turning on the square just before it", "would come beside the ball -- that is, when it is diagonally adjacent to it.", "A ray meeting two balls with one empty square between (where it's path would", "pass between them) would not know which way to turn, and will be reflected", "back 180 degrees. Finally, if there is a ball on the square next to the one", "the ray is entering onto, so that the ray would have to turn before entering", "the box (an impossibility), it is reflected.", " ", " This may sound confusing, but will become clear after a few games.", /* End of first help screen */ " When you launch a ray, its entry and exit point will be marked. If it went", "in and was absorbed, the entry point is marked with the letter `H.' If it went", "in and was reflected (came back out the same port), the entry/exit is marked", "with an `R.' Otherwise, the entry and exit ports are marked with the same", "capital letter, beginning with an `A' for the first pair, `B' for the second", "pair, etc.", " ", " You score a penalty of one point for each port used. That is, a `hit'", "scores one point, a `reflection' scores one point, and a ray that enters and", "then leaves by another port scores two points. When you ask the computer to", "check your solution, any bad guesses (balls in the wrong place, or missing", "balls) count five points. The lower your score, the better!", /* End of 2nd help screen */ " Follow the on-screen menu to solve the puzzle. When you elect to launch a", "ray, an arrow cursor will show you where it will enter -- you move this arrow", "to the desired location with the cursor control keys. Likewise, you move the", "flashing block cursor to the desired location in the grid to place or remove a", "ball. You can clear the whole field at one swipe with the `F4' key.", " ", " Note, you use the function keys to tell the computer what you want to do;", "then you use the cursor control keys to position the arrow or flashing cursor", "to the desired entry port or ball location; then, use the `Enter' key to", "actually launch the ray or place (or remove) the ball.", " ", " When you think you have it solved, press `F5' and the computer will show you", "the result. Bad guesses are marked by reverse `happy faces', good guesses by", "low-intensity `happy faces', and balls you missed by high-intensity `happy", "faces.' If you are playing a color version, the guesses are in yellow before", "scoring, and turn green if they are correct; wrong guesses are red `reverse", "happy faces' and missed balls red `happy faces.'", " ", "You may choose to hide from one to nine balls; less than three is trivial,", "and more than five usually has ambiguous balls (hidden behind other balls so", "that their location cannot be deduced from the rays). Good luck!" }; set_cursor(-1,0); cls(); for(i=0,j=1;i < 21;i++,j++) write_scr(1,j,LO,message[i]); write_scr(20,23,LO, cont_message); getch(); cls(); for(i=21,j=1;i < 33;i++,j++) write_scr(1,j,LO,message[i]); write_scr(20,23,LO, ""); getch(); sample_game(); cls(); for(i=33,j=1;i < 54;i++,j++) write_scr(1,j,LO,message[i]); write_scr(20,23,LO, cont_message); getch(); } void stall(int delay) { clock_t end; end = time(NULL) + delay; for ( ;(time(NULL) < end);); } /* ** print port labels ** */ void print_ports(unsigned char plabel[33][3]) { int i; for (i = 1;i < 9;i++) { write_scr(25 +4*i, 4, LO, plabel[i]); write_scr(62, 4 + 2*i, LO, plabel[i + 8]); write_scr(25 +4*i,22, LO, plabel[25-i]); write_scr(25, 4 + 2*i, LO, plabel[33 - i]); } } void draw_gun(int col, int row, int work) { int x, y; unsigned char buff[3]; if (row == 0) { x = 26 + col * 4, y = 3; if (work) strcpy(buff,"\x19"); /* Down arrow */ else strcpy(buff," "); } if (col == 9) { x = 64, y = 4 + 2 * row; if (work) strcpy(buff,"\x1b-"); /* Left arrow */ else strcpy(buff," "); } if (row == 9) { x = 26 + col * 4, y = 23; if (work) strcpy(buff,"\x18"); /* Up arrow */ else strcpy(buff," "); } if (col == 0) { x = 24, y = 4 + 2 * row; if (work) strcpy(buff,"-\x1a"); /* Right arrow */ else strcpy(buff," "); } write_scr(x, y, LO, buff); } void set_cursor(int hm, int lm) { _asm { mov bh,0 ; Page 0 mov ch,hm mov cl,lm mov ah,1 ; Service 1, 10h int 10h ; Set Cursor type } }