|
|
#include <stdio.h>
|
|
|
#include <stdlib.h>
|
|
|
#include <termios.h>
|
|
|
#include <unistd.h>
|
|
|
#include <time.h>
|
|
|
#include <ctype.h>
|
|
|
#include <string.h>
|
|
|
#include <pthread.h>
|
|
|
|
|
|
#define size 20
|
|
|
#define mines 60
|
|
|
|
|
|
int tilesLeft = (size * size) - mines;
|
|
|
int flags = 0;
|
|
|
|
|
|
int cursorX = 0;
|
|
|
int cursorY = 0;
|
|
|
|
|
|
int timerSeconds = 0;
|
|
|
int timerMinutes = 0;
|
|
|
|
|
|
|
|
|
//Status hry: 0 = hra nezačala, 1 = výhra, 2 = prohra, 3 = hra běží
|
|
|
int gameStatus = 0;
|
|
|
|
|
|
char tileField[size][size];
|
|
|
char mineLocations[size][size];
|
|
|
char mineCount[size][size];
|
|
|
|
|
|
char covered = 'c';
|
|
|
char uncovered = '0';
|
|
|
char mine = 'm';
|
|
|
char flag = 'f';
|
|
|
|
|
|
|
|
|
//EMOJI ZOOOOOOOONA
|
|
|
//Znaky, které do konzole vypisuje funkce drawField()
|
|
|
char coveredTile[] = "🌫️";
|
|
|
char mineTile[] = "💣";
|
|
|
//char cursorTile[] = "💠"; - Nakonec použita barva pozadí místo tohoto
|
|
|
char flagTile[] = "🚩";
|
|
|
char zeroTile[] = "⬛";
|
|
|
char oneTile[] = "1️⃣";
|
|
|
char twoTile[] = "2️⃣";
|
|
|
char threeTile[] = "3️⃣";
|
|
|
char fourTile[] = "4️⃣";
|
|
|
char fiveTile[] = "5️⃣";
|
|
|
char sixTile[] = "6️⃣";
|
|
|
char sevenTile[] = "7️⃣";
|
|
|
char eightTile[] = "8️⃣";
|
|
|
|
|
|
|
|
|
int generateStartingField();
|
|
|
int drawField();
|
|
|
int monitorKeys();
|
|
|
int generateMines();
|
|
|
int disableCanonicalMode();
|
|
|
int moveCursorDown();
|
|
|
int moveCursorUp();
|
|
|
int moveCursorLeft();
|
|
|
int moveCursorRight();
|
|
|
int uncoverTiles();
|
|
|
int flagSwitch();
|
|
|
int uncoverMines();
|
|
|
int countMines();
|
|
|
int fastUncover();
|
|
|
int fastUncoverUncoverAndContinue();
|
|
|
int win();
|
|
|
int prepareGame();
|
|
|
int checkForBomb();
|
|
|
int checkFlags();
|
|
|
int startTimer();
|
|
|
|
|
|
|
|
|
int main(){
|
|
|
//Příprava hry
|
|
|
srand(time(NULL));
|
|
|
generateStartingField();
|
|
|
generateMines();
|
|
|
countMines();
|
|
|
drawField();
|
|
|
struct termios orig_termios;
|
|
|
disableCanonicalMode(&orig_termios);
|
|
|
|
|
|
//Průběh hry
|
|
|
monitorKeys();
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Hlavní herní funkce, která udržuje hru a spouští další funkce
|
|
|
int monitorKeys(){
|
|
|
|
|
|
char keyPressed = getchar();
|
|
|
switch(tolower(keyPressed)){
|
|
|
case 'w':
|
|
|
moveCursorUp();
|
|
|
break;
|
|
|
case 'a':
|
|
|
moveCursorLeft();
|
|
|
break;
|
|
|
case 'd':
|
|
|
moveCursorRight();
|
|
|
break;
|
|
|
case 's':
|
|
|
moveCursorDown();
|
|
|
break;
|
|
|
case 'f':
|
|
|
flagSwitch();
|
|
|
break;
|
|
|
case 'e':
|
|
|
case ' ':
|
|
|
uncoverTiles();
|
|
|
}
|
|
|
|
|
|
if(tilesLeft < 1){
|
|
|
win();
|
|
|
}
|
|
|
drawField();
|
|
|
monitorKeys();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
void* timer(void* arg) {
|
|
|
while (1) {
|
|
|
sleep(1);
|
|
|
timerSeconds++;
|
|
|
if(timerSeconds > 59){
|
|
|
timerSeconds = 0;
|
|
|
timerMinutes++;
|
|
|
}
|
|
|
system("clear");
|
|
|
drawField();
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
int prepareGame(){
|
|
|
generateMines();
|
|
|
countMines();
|
|
|
gameStatus = 3;
|
|
|
if(mineCount[cursorY][cursorX] == '0'){
|
|
|
|
|
|
startTimer();
|
|
|
return 0;
|
|
|
}
|
|
|
prepareGame();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Vygeneruje počáteční pole
|
|
|
int generateStartingField(){
|
|
|
int i = 0;
|
|
|
|
|
|
while(i < size){
|
|
|
|
|
|
int ii = 0;
|
|
|
|
|
|
while(ii < size){
|
|
|
tileField[i][ii] = covered;
|
|
|
ii++;
|
|
|
}
|
|
|
i++;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Vygeneruje rozmíštění min
|
|
|
int generateMines(){
|
|
|
int i = 0;
|
|
|
while(i < size){
|
|
|
int ii = 0;
|
|
|
while(ii < size){
|
|
|
mineLocations[i][ii] = '0';
|
|
|
ii++;
|
|
|
}
|
|
|
i++;
|
|
|
}
|
|
|
i = 0;
|
|
|
while(i != mines){
|
|
|
int mineX = rand() % size;
|
|
|
int mineY = rand() % size;
|
|
|
mineLocations[mineY][mineX] = 'm';
|
|
|
i++;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Spočítá počet min v okolí všech bloků a uloží pro pozdější užití
|
|
|
int countMines(){
|
|
|
int i = 0;
|
|
|
while(i < size){
|
|
|
int ii = 0;
|
|
|
while(ii < size){
|
|
|
//Pokud je na pozici miny tak skip
|
|
|
if(mineLocations[i][ii] == 'm'){
|
|
|
ii++;
|
|
|
continue;
|
|
|
}
|
|
|
int minesNextToTile = 0;
|
|
|
minesNextToTile = checkForBomb(ii-1, i, minesNextToTile);
|
|
|
minesNextToTile = checkForBomb(ii, i-1, minesNextToTile);
|
|
|
minesNextToTile = checkForBomb(ii-1, i-1, minesNextToTile);
|
|
|
minesNextToTile = checkForBomb(ii+1, i-1, minesNextToTile);
|
|
|
minesNextToTile = checkForBomb(ii-1, i+1, minesNextToTile);
|
|
|
minesNextToTile = checkForBomb(ii+1, i+1, minesNextToTile);
|
|
|
minesNextToTile = checkForBomb(ii, i+1, minesNextToTile);
|
|
|
minesNextToTile = checkForBomb(ii+1, i, minesNextToTile);
|
|
|
mineCount[i][ii] = '0' + minesNextToTile;
|
|
|
ii++;
|
|
|
}
|
|
|
i++;
|
|
|
}
|
|
|
}
|
|
|
int checkForBomb(int x, int y, int numberOfMines){
|
|
|
if(x < 0 || x >= size || y < 0 || y >= size || mineLocations[y][x] != 'm'){
|
|
|
return numberOfMines;
|
|
|
}
|
|
|
return numberOfMines+1;
|
|
|
}
|
|
|
|
|
|
//Překreslí hrací pole to terminálu
|
|
|
int drawField(){
|
|
|
system("clear");
|
|
|
int i = 0;
|
|
|
int ii = 0;
|
|
|
int fieldSize = sizeof(tileField) / sizeof(tileField[0]);
|
|
|
printf("\n");
|
|
|
while(i < fieldSize){
|
|
|
ii = 0;
|
|
|
while(ii < fieldSize){
|
|
|
|
|
|
|
|
|
if(gameStatus == 2){
|
|
|
printf("\033[%dm", 40 + 1);
|
|
|
}
|
|
|
if(gameStatus == 1){
|
|
|
printf("\033[%dm", 40 + 2);
|
|
|
}
|
|
|
if (i == cursorY && ii == cursorX){
|
|
|
printf("\033[%dm", 40 + 4);
|
|
|
}
|
|
|
switch (tileField[i][ii]){
|
|
|
case 'c':
|
|
|
printf(coveredTile);
|
|
|
break;
|
|
|
case 'm':
|
|
|
printf(mineTile);
|
|
|
break;
|
|
|
case 'f':
|
|
|
printf(flagTile);
|
|
|
break;
|
|
|
case '0':
|
|
|
printf(zeroTile);
|
|
|
break;
|
|
|
case '1':
|
|
|
printf(oneTile);
|
|
|
break;
|
|
|
case '2':
|
|
|
printf(twoTile);
|
|
|
break;
|
|
|
case '3':
|
|
|
printf(threeTile);
|
|
|
break;
|
|
|
case '4':
|
|
|
printf(fourTile);
|
|
|
break;
|
|
|
case '5':
|
|
|
printf(fiveTile);
|
|
|
break;
|
|
|
case '6':
|
|
|
printf(sixTile);
|
|
|
break;
|
|
|
case '7':
|
|
|
printf(sevenTile);
|
|
|
break;
|
|
|
case '8':
|
|
|
printf(eightTile);
|
|
|
break;
|
|
|
default:
|
|
|
printf("n");
|
|
|
}
|
|
|
printf(" \033[%dm", 40 + 0);
|
|
|
ii++;
|
|
|
}
|
|
|
|
|
|
printf("\n");
|
|
|
i++;
|
|
|
}
|
|
|
printf("\n\n\033[%dm", 47);
|
|
|
printf(" \033[%dm", 30);
|
|
|
printf("POHYB = WASD, VLAJKA = F, ODKRÝT = E nebo SPACE");
|
|
|
printf(" \033[%dm", 40);
|
|
|
printf("\n\n\033[%dm", 34);
|
|
|
printf("\e[1mZbývá min: ");
|
|
|
printf("\033[0;31m%d", mines-flags);
|
|
|
printf("\n\033[0;34m\e[1mČas: \033[0;31m");
|
|
|
printf("%d", timerMinutes);
|
|
|
printf(":");
|
|
|
|
|
|
//Protože richard je perfekcionalista
|
|
|
if(timerSeconds < 10){
|
|
|
printf("0");
|
|
|
}
|
|
|
printf("%d", timerSeconds);
|
|
|
printf("\033[0;30m\n\n");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Vypnutí can. módu, aby bylo možné zaznamenávat stisk kláves
|
|
|
int disableCanonicalMode(struct termios *orig_termios){
|
|
|
struct termios newTermios;
|
|
|
tcgetattr(STDIN_FILENO, orig_termios);
|
|
|
newTermios = *orig_termios;
|
|
|
newTermios.c_lflag &= ~(ICANON | ECHO);
|
|
|
newTermios.c_cc[VMIN] = 1;
|
|
|
newTermios.c_cc[VTIME] = 0;
|
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
|
|
|
}
|
|
|
|
|
|
|
|
|
//Funkce pro pohybování kurzorem
|
|
|
int moveCursorDown(){
|
|
|
if(cursorY != (size - 1)){
|
|
|
cursorY++;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
int moveCursorUp(){
|
|
|
if(cursorY != 0){
|
|
|
cursorY--;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
int moveCursorLeft(){
|
|
|
if(cursorX != 0){
|
|
|
cursorX--;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
int moveCursorRight(){
|
|
|
if(cursorX != (size - 1)){
|
|
|
cursorX++;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Označení tile vlaječkou :3
|
|
|
int flagSwitch(){
|
|
|
startTimer();
|
|
|
char tile = tileField[cursorY][cursorX];
|
|
|
if(tile == covered){
|
|
|
tileField[cursorY][cursorX] = flag;
|
|
|
flags++;
|
|
|
}
|
|
|
if(tile == flag ){
|
|
|
tileField[cursorY][cursorX] = covered;
|
|
|
flags--;
|
|
|
}
|
|
|
if(flags == mines){
|
|
|
checkFlags();
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Odekreje tile nebo ukončí hru pokud narazí na bombu
|
|
|
int uncoverTiles(){
|
|
|
if(gameStatus == 0){
|
|
|
prepareGame();
|
|
|
}
|
|
|
if(tileField[cursorY][cursorX] == 'f'){
|
|
|
return 0;
|
|
|
}
|
|
|
if(mineLocations[cursorY][cursorX] == 'm'){
|
|
|
gameStatus = 2;
|
|
|
uncoverMines();
|
|
|
printf("\033[%dm", 30 + 1);
|
|
|
printf("\nProhrál jsi!\n");
|
|
|
exit(0);
|
|
|
}
|
|
|
|
|
|
tileField[cursorY][cursorX] = mineCount[cursorY][cursorX];
|
|
|
tilesLeft--;
|
|
|
fastUncover(cursorX, cursorY);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
//Odhalí všechny miny
|
|
|
int uncoverMines(){
|
|
|
int i = 0;
|
|
|
|
|
|
while(i < size){
|
|
|
|
|
|
int ii = 0;
|
|
|
|
|
|
while(ii < size){
|
|
|
if(mineLocations[i][ii] == 'm'){
|
|
|
tileField[i][ii] = mine;
|
|
|
}
|
|
|
ii++;
|
|
|
}
|
|
|
i++;
|
|
|
}
|
|
|
|
|
|
drawField();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int fastUncover(int x, int y){
|
|
|
if(mineCount[y][x] != '0'){
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
fastUncoverUncoverAndContinue(x-1,y);
|
|
|
fastUncoverUncoverAndContinue(x,y-1);
|
|
|
fastUncoverUncoverAndContinue(x-1,y-1);
|
|
|
fastUncoverUncoverAndContinue(x+1,y-1);
|
|
|
fastUncoverUncoverAndContinue(x-1,y+1);
|
|
|
fastUncoverUncoverAndContinue(x+1,y+1);
|
|
|
fastUncoverUncoverAndContinue(x+1,y);
|
|
|
fastUncoverUncoverAndContinue(x,y+1);
|
|
|
return 0;
|
|
|
}
|
|
|
int fastUncoverUncoverAndContinue(int x, int y){
|
|
|
|
|
|
if(!(tileField[y][x] == covered || tileField[y][x] == flag) || x < 0 || x >= size || y < 0 || y >= size){
|
|
|
return 0;
|
|
|
}
|
|
|
tilesLeft--;
|
|
|
tileField[y][x] = mineCount[y][x];
|
|
|
fastUncover(x, y);
|
|
|
return 0;
|
|
|
}
|
|
|
int win(){
|
|
|
gameStatus = 1;
|
|
|
uncoverMines();
|
|
|
printf("\033[%dm", 30 + 2);
|
|
|
printf("\nVyhrál jsi!\n");
|
|
|
exit(0);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Zkontroluje zda jsou flagy správně dané na minách
|
|
|
int checkFlags(){
|
|
|
int i = 0;
|
|
|
while(i < size){
|
|
|
int ii = 0;
|
|
|
while(ii < size){
|
|
|
if(tileField[i][ii] != flag){
|
|
|
ii++;
|
|
|
continue;
|
|
|
}
|
|
|
if(mineLocations[i][ii] != 'm'){
|
|
|
return 0;
|
|
|
}
|
|
|
ii++;
|
|
|
}
|
|
|
i++;
|
|
|
}
|
|
|
win();
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
//Zapnout časoměru
|
|
|
int startTimer(){
|
|
|
if(timerSeconds == 0 && timerMinutes == 0){
|
|
|
pthread_t thread_id;
|
|
|
int interval = 2;
|
|
|
pthread_create(&thread_id, NULL, timer, &interval);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|