/* --------------------------------------------------- MINESWEEPER fuer die Konsole 04.11.2015 R . Seelig Aenderungen: 22.08.2025 Rekursion entfernt Ueberschrift entfernt Farbentools eingebettet --------------------------------------------------- */ #include #include #include #include #include #include // fuer getch() #include #include #include #define feldw 2 #define mfeldx 18 #define mfeldy 18 #define xofs 14 #define yofs 2 #define bombanz 40 #define win1x 2 #define win1y 3 #define deckchar '%' #define markchar '+' #define falschmark 'X' #define bombchar 'B' // besser ASCII 208 #define getreten 'B' #define mfsize (mfeldx * mfeldy) // fuer die nichtrekursive Fuellfunktion benoetigt es eines virtuellen Stacks. Die // Groesse dieses Stacks wurde fuer ein "Minenfeld" mit 256 Feldern mit 407 ermittelt // (hier Dank an Yalu aus www.mikrocotroller.net), Groessere Felder benoetigen hier // dann eine groessere Angabe #define spmaxsize 407 uint8_t tomarkb; // Farbattribute : gedecktes Feld, Ziffern 1..8, // Leerfeld, Markierung, Mine, falsche Markierung, "draufgetreten" uint8_t minecol[14] = { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x08, 0x0c, 0x04, 0x0c, 0x0c }; /* ######################################################################################### Prototypen, Variable und Defines fuer Farben und Tastenlesen in der Konsole ######################################################################################### */ #define black 0 #define blue 1 #define green 2 #define cyan 3 #define red 4 #define magenta 5 #define brown 6 #define grey 7 #define darkgrey 8 #define lightblue 9 #define lightgreen 10 #define lightcyan 11 #define lightred 12 #define lightmagenta 13 #define yellow 14 #define white 15 #define keyleft 0x00005b44 #define keyright 0x00005b43 #define keyup 0x00005b41 #define keydown 0x00005b42 #define pageup 0x005b357e #define pagedown 0x005b367e #define keypos1 0x00005b48 #define keyend 0x00005b46 #define esc 27 #define keyf1 0x00004f50 #define keyf2 0x00004f51 #define keyf3 0x00004f52 #define keyf4 0x00004f53 #define keyf5 0x5b31357e #define keyf6 0x5b31377e #define keyf7 0x5b31387e #define keyf8 0x5b31397e #define keyf9 0x5b32307e #define keyf10 0x5b32317e // Textfarbpalette (alte DOS-Farbenfolge seit CGA) // sw bl gn cy rt mg br gr char txpal[] = {30, 34, 32, 36, 31, 35, 33, 37}; char bkpal[] = {40, 44, 42, 46, 41, 45, 41, 47}; char axofs = 0; char ayofs = 0; uint8_t txwherex= 0; uint8_t txwherey= 0; unsigned char colattr = 0x07; // --------------- Prototypen Text ----------------- void settextcolor(unsigned char col); void setbkcolor(unsigned char col); void settextattr(unsigned char attr); void clrscr(void); void gotoxy(unsigned char x, unsigned char y); int getch(void); unsigned int readkey(void); int keypressed(void); void txbox(int x1, int y1, int x2, int y2, int f1, int f2, int f3); void txbox2(int x1, int y1, int x2, int y2, int f1, int f2, int f3); void txbox_bkg(int x1, int y1, int x2, int y2, int f3, char ch); /* ############################################################################# Ende Farbkonsole Deklarationen nach der main-Funktion ############################################################################# */ uint8_t swapnibbles(uint8_t value) { uint8_t b; b = (value & 0x0f) << 4; b |= ((value >> 4) & 0x0f); return b; } void showmfeld(char bombvisible, uint8_t *bptr) { int x, y, i; uint8_t mvalue; for (y= 1; y< mfeldy-1; y++) { for (x= 1; x< mfeldx-1; x++) { gotoxy(xofs+1+(x*feldw), yofs+1+y); mvalue= *(bptr+((y*mfeldx)+x)); switch (mvalue) { case 1 : { settextattr(minecol[1]); printf("1"); break; } case 2 : { settextattr(minecol[2]); printf("2"); break; } case 3 : { settextattr(minecol[3]); printf("3"); break; } case 4 : { settextattr(minecol[4]); printf("4"); break; } case 5 : { settextattr(minecol[5]); printf("5"); break; } case 6 : { settextattr(minecol[6]); printf("6"); break; } case 7 : { settextattr(minecol[7]); printf("7"); break; } case 8 : { settextattr(minecol[8]); printf("8"); break; } case 9 : { settextattr(minecol[9]); printf(" "); break; } default : break; } if (mvalue & 0x40) // Markierung { gotoxy(xofs+1+(x*feldw), yofs+1+y); settextattr(minecol[10]); printf("%c",markchar); } if (bombvisible) { if (mvalue & 0x80) // Mine { gotoxy(xofs+1+(x*feldw), yofs+1+y); settextattr(minecol[11]); printf("%c",bombchar); } if ((mvalue & 0xc0) == 0x40) // falsche Markierung { gotoxy(xofs+1+(x*feldw), yofs+1+y); settextattr(minecol[12]); printf("%c",falschmark); } } } } } char getnbanz(int pos, char cooked, uint8_t *bptr) // liefert die Anzahl der anliegenden Minen { char anz; anz= 0; bptr += pos; bptr -= (mfeldx+1); // Zeiger links oben von gewaehlter Position if (((*bptr) & 0xbf) == 0x80) { anz++; } bptr++; if (((*bptr) & 0xbf) == 0x80) { anz++; } bptr++; if (((*bptr) & 0xbf) == 0x80) { anz++; } bptr += (mfeldx-2); if (((*bptr) & 0xbf) == 0x80) { anz++; } bptr += 2; if (((*bptr) & 0xbf) == 0x80) { anz++; } bptr += (mfeldx-2); if (((*bptr) & 0xbf) == 0x80) { anz++; } bptr++; if (((*bptr) & 0xbf) == 0x80) { anz++; } bptr++; if (((*bptr) & 0xbf) == 0x80) { anz++; } if (cooked) if (!anz) { anz= 9; } return anz; } int movecursor(int *cpos, uint8_t *bptr) { int tast, x, y; uint8_t f1, f2, mfval; char tc; // Position im Array in x/y Koordinaten umrechnen y= *cpos / mfeldx; x= *cpos % mfeldx; do { mfval= *(bptr + (y*mfeldx)+x); // Inhalt des selektieren Feldes holen mfval &= 0x6f; // Minenanzeige fuer Cursorbewegung ausschalten f1= minecol[10]; if ((mfval & 0x40) != 0x40) { f1= minecol[mfval]; } // originale Farbzuweisung holen f2= swapnibbles(f1); // und fuer f2 Hintergrund- Vordergrundfarben tauschen gotoxy(xofs+1+(x*feldw), yofs+1+y); // Cursor auf selektierte Position setzen settextattr(0x2e); // Zeichen entsprechend der Feldbelegung schreiben if (!mfval) { tc= deckchar; } if ((mfval> 0) && (mfval< 9)) { tc= mfval + '0'; } if (mfval == 9) { tc= ' ' ; } if ((mfval & 0x40) == 0x40) { tc= markchar; } putchar(tc); gotoxy(1,1); tast= 'z'; tast= readkey(); gotoxy(xofs+1+(x*feldw), yofs+1+y); settextattr(f1); if (!mfval) { tc= deckchar; } if ((mfval> 0) && (mfval< 9)) { tc= mfval + '0'; } if (mfval == 9) { tc= ' ' ; } if ((mfval & 0x40) == 0x40) { tc= markchar; } putchar(tc); gotoxy(1,1); switch (tast) { case keyleft : if (x > 1) { x--; } break; case keyright : if (x < (mfeldx-2)) { x++; } break; case keyup : if (y > 1) { y--; } break; case keydown : if (y < (mfeldy-2)) { y++; } break; default : break; } *cpos= (y* mfeldx)+x; settextattr(7); }while (tast> 'z'); return tast; } void mark8nb(int cpos, uint8_t *bptr) // markiert 8 Nachbarn eines Nullminenfeldes { int i, cpos2; cpos2= cpos-mfeldx; // oben i= getnbanz(cpos2, 0, bptr); if ((i!= 0) && (i!= 9)) *(bptr+cpos2)= i; cpos2= cpos-1; // links i= getnbanz(cpos2, 0, bptr); if ((i!= 0) && (i!= 9)) *(bptr+cpos2)= i; cpos2 += 2; i= getnbanz(cpos2, 0, bptr); // rechts if ((i!= 0) && (i!= 9)) *(bptr+cpos2)= i; cpos2= cpos+mfeldx; // unten i= getnbanz(cpos2, 0, bptr); if ((i!= 0) && (i!= 9)) *(bptr+cpos2)= i; cpos2= cpos+mfeldx-1; // unten links i= getnbanz(cpos2, 0, bptr); if ((i!= 0) && (i!= 9)) *(bptr+cpos2)= i; cpos2= cpos+mfeldx+1; // unten rechts i= getnbanz(cpos2, 0, bptr); if ((i!= 0) && (i!= 9)) *(bptr+cpos2)= i; cpos2= cpos-mfeldx-1; // oben links i= getnbanz(cpos2, 0, bptr); if ((i!= 0) && (i!= 9)) *(bptr+cpos2)= i; cpos2= cpos-mfeldx+1; // oben rechts i= getnbanz(cpos2, 0, bptr); if ((i!= 0) && (i!= 9)) *(bptr+cpos2)= i; } /* ---------------------------------------------------------- sucht anliegende Felder ohne Minenberuehrung ---------------------------------------------------------- */ void fill4(int cpos, uint8_t *bptr) /* // rekursive Funktion, belastet den Stack sehr { int x,y; int tast; uint8_t value, ovalue; value = *(bptr+cpos); if (!value) // ohne angrenzende Minen { mark8nb(cpos, bptr); *(bptr+cpos)= 9; // als Leerfeld markieren fill4(cpos + mfeldx, bptr); // x, y+1 fill4(cpos - mfeldx, bptr); // x, y-1 fill4(cpos -1, bptr); // x-1, y fill4(cpos +1, bptr); // x+1, y } } */ { // Funktion von Yalu aus www.mikrocontroller.net ohne Rekursion static uint16_t stack[spmaxsize]; int ssize = 0; stack[ssize++] = cpos; while (ssize) { cpos = stack[--ssize]; if (!bptr[cpos]) { mark8nb(cpos, bptr); bptr[cpos] = 9; // als Leerfeld markieren stack[ssize++] = cpos - 1; // x-1, y stack[ssize++] = cpos + mfeldx; // x, y+1 stack[ssize++] = cpos - mfeldx; // x, y-1 stack[ssize++] = cpos + 1; // x+1, y } } } /* ---------------------------------------------------------- scant das Feld nach getaetigten Markierungen ---------------------------------------------------------- */ int feld_scanmarks(uint8_t *bptr) { int i, result; result= 0; for (i= 0; i< mfsize; i++) { if ((*bptr > 0) && (!(*bptr & 0xc0))) { result++; } bptr++; } return result-(2*mfeldx)-(2*mfeldy)+4; } // --------------------------------------------------------------------------------------- // intro // --------------------------------------------------------------------------------------- void intro(void) { int ch; clrscr(); settextattr(0x1e); printf("\n\r #################################################### "); printf("\n\r MineSweeper "); printf("\n\r #################################################### \n\r"); settextattr(0x2e); printf("\n\r Auf dem Spielfeld sind Minen versteckt, die es gilt "); printf("\n\r nicht zu betreten. "); printf("\n\r "); printf("\n\r Sie koennen sich mittels der Cursortasten ueber das "); printf("\n\r Feld bewegen und das Feld mit der Leertaste betreten "); printf("\n\r "); printf("\n\r Wenn Sie sich sicher sind, dass unter einem Feld eine "); printf("\n\r Mine versteckt ist, koennen Sie dieses Feld mit der "); printf("\n\r '+' Taste markieren. Ebenfalls mit der '+' Taste "); printf("\n\r koennen Sie eine gemachte Markierung wieder aufheben. "); printf("\n\r "); printf("\n\r Betreten Sie ein Feld und es ist keine Mine, wird "); printf("\n\r Ihnen angezeigt, wieviele Minen an dieses Feld an- "); printf("\n\r grenzen. "); printf("\n\r "); printf("\n\r Die ESC-Taste beendet ein laufendes Spiel. "); printf("\n\r "); printf("\n\r Finden Sie alle Minen, so haben sie das Spiel ge- "); printf("\n\r gewonnen. "); printf("\n\r "); printf("\n\r Spielestart mit beliebiger Taste... "); printf("\n\r "); settextattr(0x07); printf("\n\r"); ch= readkey(); } // --------------------------------------------------------------------------------------- // main // --------------------------------------------------------------------------------------- int main(void) { int aktpos; uint8_t mfeld[mfsize]; int i, x, y; int markanz; int ch; char forb_bpos; int zufall; int tast; char treffer; srand(time(NULL)); intro(); do { x= mfeldx / 2; y= mfeldy / 2; aktpos= (y * mfeldx) + x; tomarkb= bombanz; settextattr(0x07); for (i= 0; i< mfsize; i++) { mfeld[i]= 0x10; } for (y= 1; y < mfeldy-1; y++) { for (x= 1; x < mfeldx-1; x++) { mfeld[(y*mfeldx)+x]= 0; } } clrscr(); for (i= 0; i< bombanz; i++) { do { forb_bpos= 0; zufall= rand() % mfsize; if ((mfeld[zufall] > 0) ) { forb_bpos= 1; } // hier ist schon eine Mine // oder der Begrenzungsrand // ausserhalb des Spielfeldes } while (forb_bpos); mfeld[zufall]= 0x80; // zufaellige Mine ins Feld eintragen } settextattr(0x0a); gotoxy(xofs+13, yofs); printf("MineSweeper"); txbox2(xofs+feldw-1, yofs+1, xofs+(mfeldx * feldw)-1 ,yofs+mfeldy , 0x07, 0x07, minecol[0]); txbox_bkg(xofs+1, yofs+1, xofs+(mfeldx * feldw)-1, yofs+mfeldy, minecol[0], deckchar); txbox2(win1x, win1y, win1x+8, win1y+2, 0x07, 0x07, 0x07); settextattr(0x07); gotoxy(win1x, win1y-1); printf("zu suchen"); gotoxy(win1x+1, win1y+1); settextattr(0x74); printf(" %3i ",tomarkb); treffer= 0; do { /* // fuer Debugzwecke settextattr(0x05); gotoxy(win1x+1, win1y+5); printf("%d", feld_scanmarks(&mfeld[0])); */ showmfeld(0, &mfeld[0]); tast= movecursor(&aktpos, &mfeld[0]); if (tast== '+') { if (mfeld[aktpos] & 0x40) { mfeld[aktpos] &= 0xbf; // Userflag loeschen tomarkb++; } else { mfeld[aktpos] |= 0x40; // Userflag setzen tomarkb--; } gotoxy(win1x+1, win1y+1); settextattr(0x74); printf(" %3i ",tomarkb); } if (tast== ' ') { if (!(( mfeld[aktpos] & 0x80)== 0x80)) { i= getnbanz(aktpos, 0, &mfeld[0]); mfeld[aktpos]= i; if (i== 0) // Feld ohne angrenzende Mine { fill4(aktpos, &mfeld[0]); } } else { treffer= 1; } } ch= 0; markanz= feld_scanmarks(&mfeld[0]) + bombanz; if ( ((mfeldx-2)*(mfeldy-2)) == markanz ) { ch= 1; } }while ((tast != esc) && !treffer && !ch); settextattr(0x07); gotoxy(10,25); if (treffer) { showmfeld(1,&mfeld[0]); gotoxy(xofs+(mfeldx / 2),yofs + mfeldy + 2); settextattr(0x0c); printf("Auf Mine getreten"); y= aktpos / mfeldx; x= aktpos % mfeldx; gotoxy(1,1); gotoxy(xofs+1+(x*feldw), yofs+1+y); settextattr(minecol[13]); putchar(getreten); } if (ch) { showmfeld(1,&mfeld[0]); gotoxy(xofs+(mfeldx / 2)-2,yofs + mfeldy + 2); settextattr(0x0a); printf("Alle Minen wurden gefunden"); } gotoxy(xofs+(mfeldx / 2),yofs + mfeldy + 3); settextattr(0x07); printf("Noch einmal (J/N) ?"); tast= readkey(); }while ((tast != esc) && (tast != 'n')); clrscr(); return 0; } // -------------------------------------------------- // settextcolor // // Setzt die Textvordergrundfarbe mit der // in angegebenen Textfarbe (EGA - // kompatibel) // -------------------------------------------------- void settextcolor(unsigned char col) { char intensity; intensity= (col / 8); // if (intensity== 0) {intensity= 21;} else {intensity= 1;} printf("\033[%d;%dm",intensity,txpal[col%8]); colattr &= 0x0f; colattr |= col; } // -------------------------------------------------- // setbkcolor // // Setzt die Texthintergrundfarbe mit der // in angegebenen Textfarbe (EGA - // kompatibel) // -------------------------------------------------- void setbkcolor(unsigned char col) { char intensity; intensity= (col / 8); if (intensity== 0) {intensity= 25;} else {intensity= 5;} printf("\033[%d;%dm",bkpal[col%8],intensity); colattr &= 0x0f; colattr |= (col << 4); } // -------------------------------------------------- // clrscr // // loescht den Bildschrim mit der mit // angegebenen Farbe // -------------------------------------------------- void clrscr(void) { int i; // leider habe ich keinen anderen (schnellen) Weg (bisher) gefunden, um // den kompletten Hintergrund einzufaerben, als einfach mehere Zeilen mit // den Textattributen auf das Terminal zu schreiben und dann den Cursor // in die "Homeposition" zu bringen ... for (i= 0; i< 6; i++) { printf(" \n"); } printf("\033[2J"); printf("\033[0;0H"); gotoxy(1,1); } // -------------------------------------------------- // settextattr // // setzt die Text UND die Hintergrundfarbe mit der // in ATTR angegebenen Farbe. Die hoeherwertigen // 4 Bits bestimmen die Hintergrundfarbe, die // niederen 4 Bits die Vordergrundfarbe. // -------------------------------------------------- void settextattr(unsigned char attr) { setbkcolor(attr >> 4); settextcolor(attr & 0x0f); colattr = attr; } // -------------------------------------------------- // gotoxy // // positioniert den Textcursor an die mit x/y // angegebene Textkoordinate. // -------------------------------------------------- void gotoxy(unsigned char x, unsigned char y) { x += axofs; y += ayofs; txwherex= x; txwherey= y; printf("\033[%d;%dH",y,x); } // ------------------------------------------------ // getch // // liest eine Taste von der Tastatur ein. // WICHTIG: es wird auf einen Tastendruck // gewartet, es wird die zuletzt gedrueckte Taste // zurueck gegeben. // ------------------------------------------------ int getch(void) { static int ch = -1, fd = 0; struct termios neu, alt; fd = fileno(stdin); tcgetattr(fd, &alt); neu = alt; neu.c_lflag &= ~(ICANON|ECHO); tcsetattr(fd, TCSANOW, &neu); ch = getchar(); tcsetattr(fd, TCSANOW, &alt); ch &= 0xff; return ch; } // ------------------------------------------------ // keypressed // // Testet, ob eine Taste gedrueckt ist // WICHTIG: es wird NICHT auf einen Tastendruck // gewartet. // ------------------------------------------------ int keypressed(void) { struct termios alt, neu; int ch; int altf; tcgetattr(STDIN_FILENO, &alt); neu = alt; neu.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &neu); altf = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, altf | O_NONBLOCK); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &alt); fcntl(STDIN_FILENO, F_SETFL, altf); if(ch != EOF) { ungetc(ch, stdin); return 1; } return 0; } // -------------------------------------------------- // readkey // // Liest Tastatur ein und unterscheidet auch // zwischen Cursor(Pfeil)-Tasten und den // F1 .. F12 Tasten. Rueckgabewert ist ein // unsigned int (der bspw. bei F6 bis F12 auch in // ALLEN Stellen in voller Laenge benoetigt wird). // -------------------------------------------------- unsigned int readkey(void) { int n; unsigned int ch,ch2; ch= getch(); if (keypressed()) { ch= 0; while(keypressed()) { ch= (ch << 8) | getch(); } } return ch; } void txbox_bkg(int x1, int y1, int x2, int y2, int f3, char ch) { int x,y; settextattr(f3); if (x1> x2) {x= x2; x2= x1; x1=x;} if (y1> y2) {y= x2; y2= y1; y1=y;} x1+= feldw; y1++; for (x=x1; x< x2; x+= feldw) for (y=y1; y< y2; y++) { gotoxy(x,y); putchar(ch); } } /* ---------------------------------------------------- txbox "zeichnet" ein Textfenster mit folgendem Aussehen: x1, y1 : linke obere Textkoordinate x2, y2 : rechte untere Textkoordingate f1 : Farbattribut des linken und oberen Rahmens f2 : Farbattribut des rechten und unteren Rahmens f3 : Farbattribut des Fensterinhaltes ---------------------------------------------------- */ void txbox(int x1, int y1, int x2, int y2, int f1, int f2, int f3) { int x,y; settextattr(f3); txbox_bkg(x1, y1, x2, y2, f3, ' '); settextattr(f1); gotoxy(x1,y1); putchar(218); gotoxy(x1,y2); putchar(192); if (y2> y1+1) for (y= y1+1; y< y2; y++) { gotoxy(x1,y); putchar(179); } if (x2> x1+1) for (x= x1+1; x< x2; x++) { gotoxy(x,y1); putchar(196); } settextattr(f2); gotoxy(x2,y1); putchar(191); gotoxy(x2,y2); putchar(217); if (y2> y1+1) for (y= y1+1; y< y2; y++) { gotoxy(x2,y); putchar(179); } if (x2> x1+1) for (x= x1+1; x< x2; x++) { gotoxy(x,y2); putchar(196); } } /* ---------------------------------------------------- txbox2 "zeichnet" ein Textfenster mit folgendem Aussehen: Der Rahmen des Fensters wird mit Ascii- Zeichen < 128 dargestellt: +--+ | | +--+ Parameter dieselben wie bei txbox ---------------------------------------------------- */ void txbox2(int x1, int y1, int x2, int y2, int f1, int f2, int f3) { int x,y; settextattr(f3); txbox_bkg(x1, y1, x2, y2, f3, ' '); settextattr(f1); gotoxy(x1,y1); putchar('+'); gotoxy(x1,y2); putchar('+'); if (y2> y1+1) for (y= y1+1; y< y2; y++) { gotoxy(x1,y); putchar('|'); } if (x2> x1+1) for (x= x1+1; x< x2; x++) { gotoxy(x,y1); putchar('-'); } settextattr(f2); gotoxy(x2,y1); putchar('+'); gotoxy(x2,y2); putchar('+'); if (y2> y1+1) for (y= y1+1; y< y2; y++) { gotoxy(x2,y); putchar('|'); } if (x2> x1+1) for (x= x1+1; x< x2; x++) { gotoxy(x,y2); putchar('-'); } }