You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 lines
12 KiB
C

/*
* spielfeld.c
*
* Created on: Mar 15, 2024
* Author: Nico
*/
#include "spielfeld.h"
#include "main.h"
/*
* Beschreibung:
* Diese Funktion generiert ein Spielfeld mit der angegebenen Größe und initialisiert alle Zellen als leere Zellen.
*
* Parameter:
* size: Die Größe des Spielfelds (Anzahl der Zeilen/Spalten).
*
* Rückgabewert:
* Zeiger auf das erstellte Spielfeld (int**).
*/
int** generateGameBoard(int size) {
// Speicher für das Spielfeld reservieren
int** gameBoard = (int**)malloc(size * sizeof(int*));
for (int i = 0; i < size; i++) {
gameBoard[i] = (int*)malloc(size * sizeof(int));
}
// Initialisierung des Spielfelds
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
gameBoard[i][j] = EMPTY_CELL;
}
}
return gameBoard;
}
/*
* Beschreibung:
* Diese Funktion generiert eine zufällige ganze Zahl mithilfe des HAL-RNG-Moduls.
*
* Rückgabewert:
* Eine zufällige ganze Zahl (uint32_t).
*/
uint32_t random_int(void) {
uint32_t z;
HAL_RNG_GenerateRandomNumber(&hrng, &z);
return z;
}
/*
* Beschreibung:
* Diese Funktion platziert die Bomben zufällig auf dem Spielfeld und berechnet dann die Anzahl der umliegenden Bomben für jede Zelle.
*
* Parameter:
* gameBoard: Das Spielfeld, auf dem die Bomben platziert werden sollen.
* size: Die Größe des Spielfelds.
* numBombs: Die Anzahl der Bomben, die platziert werden sollen.
*/
void placeBombs(int** gameBoard, int size, int numBombs) {
// Zufälligen Seed initialisieren
srand(random_int());
// Platzieren der Bomben
while (numBombs > 0) {
int x = rand() % size;
int y = rand() % size;
if (gameBoard[x][y] != BOMB_CELL) {
gameBoard[x][y] = BOMB_CELL;
numBombs--;
}
}
// Berechnen der Zahlen um die Bomben herum und Markieren mit NUMBER_CELL
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (gameBoard[i][j] != BOMB_CELL) {
int count = 0;
// Überprüfe die Zellen um die aktuelle Zelle (i, j)
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
int newX = i + dx;
int newY = j + dy;
// Überprüfe, ob die Zelle innerhalb des Spielfelds liegt und eine Bombe enthält
if (newX >= 0 && newX < size && newY >= 0 && newY < size && gameBoard[newX][newY] == BOMB_CELL) {
count++;
}
}
}
// Setze die Anzahl der umliegenden Bomben in die aktuelle Zelle
gameBoard[i][j] = count;
}
}
}
}
/*
* Beschreibung:
* Diese Funktion zeigt das Spielfeld auf der UART-Schnittstelle an.
*
* Parameter:
* gameBoard: Das Spielfeld, das angezeigt werden soll.
* size: Die Größe des Spielfelds.
* numBombs: Die Gesamtanzahl der Bomben auf dem Spielfeld.
* numFlags: Die Anzahl der Flaggen, die auf dem Spielfeld platziert wurden.
*/
void displayGameBoardUART(int** gameBoard, int size, int numBombs, int numFlags) {
// Leere den Empfangspuffer der UART-Schnittstelle
clearSerialBuffer(&huart2);
char buffer[600]; // Puffer für den formatierten String
int offset = 0; // Offset für die Position im Puffer
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "\rSpielfeld:\r");
// Formatieren des Spielfelds in den Puffer
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "\r\n\n "); // Leerzeichen für die Ausrichtung
// Spaltenbezeichnungen (A, B, C, ...)
for (int i = 0; i < size; i++) {
char column = 'A' + i;
offset += snprintf(buffer + offset, sizeof(buffer) - offset, " %c", column);
}
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "\r\n");
for (int i = 0; i < size; i++) {
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "%2d ", i + 1); // Zeilennummer
for (int j = 0; j < size; j++) {
if (gameBoard[i][j] == BOMB_CELL) {
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "* "); // Bombe
} else if (gameBoard[i][j] == EMPTY_CELL) {
offset += snprintf(buffer + offset, sizeof(buffer) - offset, ". "); // Leerzelle
} else if (gameBoard[i][j] == HIDDEN_CELL) {
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "# "); // Verdeckte Zelle
} else if (gameBoard[i][j] == FLAG_CELL) {
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "? "); // Verdeckte Zelle
} else {
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "%d ", gameBoard[i][j]); // Zahl
}
}
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "\r\n");
}
// Anzeige der verbleibenden Anzahl von Bomben
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "\r\nVerbleibende Anzahl von Bomben: %d\r\n", numBombs - numFlags);
// Senden des formatierten Strings über UART
HAL_UART_Transmit(&huart2, (uint8_t*)buffer, offset, 100);
}
/*
* Werden nicht benutzt
*
*/
void freeGameBoard(int** gameBoard, int size) {
for (int i = 0; i < size; i++) {
free(gameBoard[i]);
}
free(gameBoard);
}
/*
* Werden nicht benutzt
*
*/
void freeHiddenGameBoard(int** hiddenGameBoard, int size) {
for (int i = 0; i < size; i++) {
free(hiddenGameBoard[i]);
}
free(hiddenGameBoard);
}
/*
* Beschreibung:
* Diese Funktion erstellt ein verdecktes Spielfeld mit der angegebenen Größe.
*
* Parameter:
* gameBoard: Das Spielfeld, für das ein verdecktes Spielfeld erstellt werden soll.
* size: Die Größe des Spielfelds.
*
* Rückgabewert:
* Ein Zeiger auf das erstellte verdeckte Spielfeld (int**).
*/
int** createHiddenGameBoard(int** gameBoard, int size) {
int** hiddenGameBoard = (int**)malloc(size * sizeof(int*));
for (int i = 0; i < size; i++) {
hiddenGameBoard[i] = (int*)malloc(size * sizeof(int));
}
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
hiddenGameBoard[i][j] = HIDDEN_CELL;
}
}
return hiddenGameBoard;
}
void getNewPosition(Position* pos, int dx, int dy, Position* newPos) {
newPos->col = pos->col + dy;
newPos->row = pos->row + dx;
}
/*
* Beschreibung:
* Diese Funktion deckt rekursiv alle leeren Zellen und ihre benachbarten leeren Zellen auf.
*
* Parameter:
* gameBoard: Das Spielfeld, auf dem die Operation ausgeführt wird.
* hiddenGameBoard: Das verdeckte Spielfeld, das aktualisiert wird.
* size: Die Größe des Spielfelds.
* pos: Die Position der aktuellen Zelle, die aufgedeckt wird.
*/
void revealEmptyCells(int** gameBoard, int** hiddenGameBoard, int size, Position pos) {
// Aufdecken der leeren Zelle
hiddenGameBoard[pos.row][pos.col] = gameBoard[pos.row][pos.col];
// Wenn die Zelle leer ist, müssen auch benachbarte leere Zellen aufgedeckt werden
if (hiddenGameBoard[pos.row][pos.col] == EMPTY_CELL) {
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (dx == 0 && dy == 0) continue; // Überspringe die aktuelle Zelle
Position newPos;
getNewPosition(&pos, dx, dy, &newPos);
// Überprüfen, ob die benachbarte Zelle innerhalb des Spielfelds liegt und verdeckt ist
if (newPos.row >= 0 && newPos.row < size && newPos.col >= 0 && newPos.col < size && hiddenGameBoard[newPos.row][newPos.col] == HIDDEN_CELL) {
// Wenn die benachbarte Zelle leer ist, rekursiv fortfahren
revealEmptyCells(gameBoard, hiddenGameBoard, size, newPos);
}
}
}
}
}
/*
* Beschreibung:
* Diese Funktion deckt eine bestimmte Zelle auf dem Spielfeld auf und aktualisiert das verdeckte Spielfeld entsprechend.
*
* Parameter:
* gameBoard: Das Spielfeld, auf dem die Operation ausgeführt wird.
* hiddenGameBoard: Das verdeckte Spielfeld, das aktualisiert wird.
* size: Die Größe des Spielfelds.
* row: Die Zeilennummer der aufzudeckenden Zelle.
* col: Die Spaltennummer der aufzudeckenden Zelle.
*
* Rückgabewert:
* 1, wenn die Zelle erfolgreich aufgedeckt wurde.
* 0, wenn die Zelle eine Bombe war und das Spiel dadurch verloren wurde.
*/
int revealCell(int** gameBoard, int** hiddenGameBoard, int size, int row, int col) {
Position pos;
pos.row = row;
pos.col = col;
// Überprüfen, ob die Position im gültigen Bereich liegt
if (pos.row < 0 || pos.row >= size || pos.col < 0 || pos.col >= size) {
return 1;
}
// Überprüfen, ob die Zelle bereits aufgedeckt ist
if (hiddenGameBoard[pos.row][pos.col] != HIDDEN_CELL) {
return 1;
}
if (hiddenGameBoard[pos.row][pos.col] == FLAG_CELL){
return 1;
}
if (gameBoard[pos.row][pos.col] == BOMB_CELL) {
hiddenGameBoard[pos.row][pos.col] = gameBoard[pos.row][pos.col];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (gameBoard[i][j] == BOMB_CELL) {
hiddenGameBoard[i][j] = gameBoard[i][j];
}
}
}
handleBombExploded();
return 0;
}
// Wenn die Zelle leer ist, müssen auch benachbarte leere Zellen aufgedeckt werden
if (gameBoard[pos.row][pos.col] == EMPTY_CELL) {
revealEmptyCells(gameBoard, hiddenGameBoard, size, pos);
} else {
// Aufdecken der Zelle im verdeckten Spielfeld
hiddenGameBoard[pos.row][pos.col] = gameBoard[pos.row][pos.col];
}
return 1;
}
/*
* Beschreibung:
* Diese Funktion platziert oder entfernt eine Flagge auf einer bestimmten Zelle des verdeckten Spielfelds.
*
* Parameter:
* hiddenGameBoard: Das verdeckte Spielfeld, auf dem die Operation ausgeführt wird.
* row: Die Zeilennummer der Zelle, auf der die Flagge platziert/entfernt werden soll.
* col: Die Spaltennummer der Zelle, auf der die Flagge platziert/entfernt werden soll.
* numFlags: Ein Zeiger auf die Anzahl der Flaggen auf dem Spielfeld.
*/
void setFlag(int** hiddenGameBoard, int row, int col, int* numFlags) {
if (hiddenGameBoard[row][col] == FLAG_CELL)
{
hiddenGameBoard[row][col] = HIDDEN_CELL;
(*numFlags)--;
}
else if (hiddenGameBoard[row][col] == HIDDEN_CELL)
{
hiddenGameBoard[row][col] = FLAG_CELL;
(*numFlags)++;
}
}
/*
* Beschreibung:
* Diese Funktion überprüft, ob das Spiel gewonnen wurde, indem sie prüft, ob alle Bomben markiert wurden und alle nicht-Bomben-Zellen aufgedeckt sind.
*
* Parameter:
* gameBoard: Das Spielfeld mit den Bomben.
* hiddenGameBoard: Das verdeckte Spielfeld.
* size: Die Größe des Spielfelds.
* numBombs: Die Anzahl der Bomben auf dem Spielfeld.
* numFlags: Die Anzahl der Flaggen auf dem Spielfeld.
*
* Rückgabewert:
* 1, wenn das Spiel gewonnen wurde.
* 0, wenn das Spiel noch nicht gewonnen wurde.
*/
int checkWin(int** gameBoard, int** hiddenGameBoard, int size, int numBombs, int numFlags) {
// Überprüfen, ob alle Bombenpositionen mit einer Flagge markiert wurden
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (gameBoard[i][j] == BOMB_CELL && hiddenGameBoard[i][j] != FLAG_CELL) {
return 0; // Nicht gewonnen, da nicht alle Bomben markiert sind
}
}
}
// Überprüfen, ob alle nicht-Bombenfelder aufgedeckt wurden
int totalCells = size * size;
int uncoveredCells = 0;
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (gameBoard[i][j] != BOMB_CELL && hiddenGameBoard[i][j] != HIDDEN_CELL) {
uncoveredCells++;
}
}
}
// Das Spiel ist gewonnen, wenn alle nicht-Bombenfelder aufgedeckt wurden und alle Bomben mit einer Flagge markiert wurden
if (uncoveredCells == totalCells - numBombs && numFlags == numBombs) {
char message[] = "Herzlichen Glückwunsch! Du hast gewonnen!\r\nStarte neu mit der ON Taste.\r\n";
HAL_UART_Transmit(&huart2, (uint8_t*)message, sizeof(message), 100);
return 1;
}
return 0;
}
/*
* Beschreibung:
* Diese Funktion gibt eine Meldung aus, dass das Spiel verloren wurde, da eine Bombe explodiert ist. *
*/
void handleBombExploded() {
char message[] = "Spiel Verloren! Du hast eine Bombe getroffen!\r\nStarte neu mit der ON Taste. \r\n";
HAL_UART_Transmit(&huart2, (uint8_t*)message, sizeof(message), 100);
}