From ab10d16d9eb73e9ff38a56a5fc2fac7d692216dd Mon Sep 17 00:00:00 2001 From: Andreas Deitche Date: Mon, 30 Mar 2026 16:05:45 +0000 Subject: [PATCH] Dateien nach "/" hochladen --- game.c | 23 +++ game.h | 11 ++ graphicalGame.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++++ graphicalGame.h | 8 + input.c | 12 ++ 5 files changed, 509 insertions(+) create mode 100644 game.c create mode 100644 game.h create mode 100644 graphicalGame.c create mode 100644 graphicalGame.h create mode 100644 input.c diff --git a/game.c b/game.c new file mode 100644 index 0000000..b957837 --- /dev/null +++ b/game.c @@ -0,0 +1,23 @@ +#include "game.h" +#include +#include +#include + +#define MAX_RAND_TRIES_PER_WORD 10 +#define EMPTY_CHAR 0 + +//TODO: Spiellogik implementieren: +/* * Wörter aus der Wortliste zufällig horizontal oder vertikal platzieren + * restliche Felder mit zufälligen Buchstaben füllen */ + +// Creates the word salad by placing words randomly and filling empty spaces +int createWordSalad(char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldLen, const char words[][MAX_WORD_LEN], unsigned int wordCount) +{ + +} + +// Prints the word salad to console +void showWordSalad(const char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldLen) +{ + +} diff --git a/game.h b/game.h new file mode 100644 index 0000000..c48d918 --- /dev/null +++ b/game.h @@ -0,0 +1,11 @@ +#ifndef GAME_H +#define GAME_H + +#include "input.h" + +#define MAX_SEARCH_FIELD_LEN 100 + +int createWordSalad(char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldLen, const char words[][MAX_WORD_LEN], unsigned int wordCount); +void showWordSalad(const char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldLen); + +#endif diff --git a/graphicalGame.c b/graphicalGame.c new file mode 100644 index 0000000..b479304 --- /dev/null +++ b/graphicalGame.c @@ -0,0 +1,455 @@ +#include +#include + +#include "graphicalGame.h" +#include "raylib.h" + +#define MAX_MESSAGE_LEN 256 +#define MAX_SOLUTION_WORD_LEN 16 + +typedef struct +{ + Vector2 startPosition; + Vector2 endPosition; + int isSelected; +} MouseSelection; + +typedef struct +{ + Vector2 position; + char character[2]; + int isMarked; +} CharSquare; + +typedef struct +{ + CharSquare *squares; + unsigned int count; + unsigned int squareSize; + unsigned int fontSize; + Vector2 position; + Vector2 size; +} CharSquarePanel; + +typedef struct +{ + char content[MAX_WORD_LEN]; + char *solution; + Vector2 position; + int wasFound; +} SearchWord; + +typedef struct +{ + SearchWord *words; + unsigned int count; + int fontSize; + Vector2 position; + Vector2 size; +} SearchWordPanel; + +typedef struct +{ + char content[MAX_MESSAGE_LEN]; + Vector2 position; + unsigned int size; +} WinMessage; + +typedef struct +{ + char text[MAX_MESSAGE_LEN]; + Vector2 position; + Vector2 size; + unsigned int fontSize; +} HelperMessage; + +// Creates a helper message to guide the user +static HelperMessage createHelperMessage(unsigned int screenWidth) +{ + const char *text = "Please search below for the words located at the bottom \nand draw a line exactly on the desired characters ..."; + HelperMessage msg = {"", {0, 0}, {screenWidth, 0}, 18}; + + // Copy text into msg, ensuring does not exceed max length + strncpy(msg.text, text, MAX_MESSAGE_LEN); + msg.text[MAX_MESSAGE_LEN-1] = '\0'; + + // Set the vertical size based on font size + msg.size.y = 3 * msg.fontSize; + + return msg; +} + +// Creates a winning message when the user wins +static WinMessage createWinMessage(unsigned int screenSize) +{ + WinMessage winMsg; + char *text = "Congratulations! You won!"; + + strncpy(winMsg.content, text, MAX_MESSAGE_LEN); + winMsg.content[MAX_MESSAGE_LEN-1] = '\0'; + winMsg.size = 30; // Set font size + + // Calculate x and y positions for centering the message + winMsg.position.x = (screenSize - strlen(winMsg.content)*winMsg.size*0.52) / 2; + winMsg.position.y = screenSize / 2; + + return winMsg; +} + +// Frees memory associated with a search word panel +static void freeSearchWordPanel(SearchWordPanel *panel) +{ + for(int i = 0; panel->words != NULL && i < panel->count; i++) + free(panel->words[i].solution); // Free solution strings + free(panel->words); // Free word array + panel->words = NULL; + panel->count = 0; + panel->size.x = 0; + panel->size.y = 0; +} + +// Creates a panel to display a list of search words +static SearchWordPanel createSearchWordPanel(const char words[][MAX_WORD_LEN], unsigned int numberOfWords, unsigned int windowOffset) +{ + const int maxStringLenInPx = 200; // Max width of each word + const int fontSize = 18; // Font size for displaying words + const int rowHeight = fontSize * 1.2 + 5; // Height of each row of words + + SearchWordPanel panel = {NULL, 0, fontSize, {0, windowOffset}, {windowOffset, 0}}; + + unsigned int xOffset = 5; + unsigned int yOffset = 15; + + // Allocate memory for words if any are present + if(numberOfWords > 0) + panel.words = (SearchWord *)malloc(sizeof(SearchWord) * numberOfWords); + + // If memory allocation is successful + if(panel.words != NULL) + { + // Loop through and set up the words and their positions + for(int i = 0; i < numberOfWords; i++) + { + strncpy(panel.words[i].content, words[i], MAX_SOLUTION_WORD_LEN); + panel.words[i].content[MAX_SOLUTION_WORD_LEN-1] = '\0'; + + // Truncate word if exceeds max length + if(strlen(words[i]) > MAX_SOLUTION_WORD_LEN-1) + strncpy(panel.words[i].content+MAX_SOLUTION_WORD_LEN-4, "...", 4); + + // Allocate memory for solution word + panel.words[i].solution = (char *)malloc(sizeof(char) * (strlen(words[i]) + 1)); + + if(panel.words[i].solution != NULL) + strcpy(panel.words[i].solution, words[i]); + else + { + freeSearchWordPanel(&panel); // Free memory in case of failure + numberOfWords = 0; + break; + } + + panel.words[i].wasFound = 0; // Initialize "found" flag + panel.words[i].position.x = xOffset; + panel.words[i].position.y = yOffset; + + // Move to next position for next word + xOffset += maxStringLenInPx + 5; + + // Move to next row if needed + if(xOffset > windowOffset) + { + xOffset = 5; + yOffset += rowHeight; + } + } + panel.count = numberOfWords; // Sets total word count + + // Adjust panel size based on last word's position + if(numberOfWords > 0) + panel.size.y = panel.words[numberOfWords - 1].position.y + rowHeight; + } + + return panel; +} + +// Creates a square for a character in the search grid +static CharSquare createSquare(unsigned int rowIdx, unsigned int colIdx, char character, unsigned int squareSize) +{ + CharSquare square; + square.position.x = colIdx * squareSize; + square.position.y = rowIdx * squareSize; + square.character[0] = character; + square.character[1] = '\0'; + square.isMarked = 0; // Mark as unmarked initially + + return square; +} + +// Creates a panel of character squares (the search grid) +static CharSquarePanel createCharSquarePanel(const char wordSalad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldSizeInChars, int panelSizeInPx) +{ + CharSquarePanel squarePanel; + squarePanel.squares = (CharSquare *)malloc(sizeof(CharSquare) * searchFieldSizeInChars * searchFieldSizeInChars); + squarePanel.count = 0; + squarePanel.squareSize = (double)panelSizeInPx / searchFieldSizeInChars; // Calculate the square size + squarePanel.fontSize = squarePanel.squareSize * 0.75; // Set font size relative to square size + squarePanel.position.x = 0; + squarePanel.position.y = 0; + squarePanel.size.x = panelSizeInPx; + squarePanel.size.y = panelSizeInPx; + + // If memory for squares is allocated successfully loop through grid and create squares for each character + if(squarePanel.squares != NULL) + { + for(int i = 0; i < searchFieldSizeInChars; i++) + { + for(int j = 0; j < searchFieldSizeInChars; j++) + { + squarePanel.squares[squarePanel.count] = createSquare(i, j, wordSalad[i][j], squarePanel.squareSize); + squarePanel.count++; + } + } + } + + return squarePanel; +} + +// Frees memory associated with CharSquarePanel +static void freeCharSquarePanel(CharSquarePanel *squarePanel) +{ + free(squarePanel->squares); // Free squares array + squarePanel->squares = NULL; + squarePanel->count = 0; // Reset count +} + +// Draws all squares of CharSquarePanel +static void drawSquares(const CharSquarePanel squarePanel) +{ + float fontOffset = squarePanel.squareSize / 4; // Offset for font positioning + + // Loop through all squares and draw them + for(int i = 0; i < squarePanel.count; i++) + { + CharSquare square = squarePanel.squares[i]; + + Vector2 squareScreenCoord = {squarePanel.position.x + square.position.x, squarePanel.position.y + square.position.y}; + Color squareColor = DARKBLUE; + Color fontColor = LIGHTGRAY; + + // Change colors if is marked + if(square.isMarked) + { + squareColor = GREEN; + fontColor = BLACK; + } + + // Draw square and its border + DrawRectangle(squareScreenCoord.x, squareScreenCoord.y, squarePanel.squareSize, squarePanel.squareSize, squareColor); + for(int i = 1; i <= 3; i++) + DrawRectangleLines(squareScreenCoord.x, squareScreenCoord.y, squarePanel.squareSize-i, squarePanel.squareSize-i, LIGHTGRAY); + + // Draw character inside the square + DrawText(square.character, squareScreenCoord.x + fontOffset, squareScreenCoord.y + fontOffset, squarePanel.fontSize, fontColor); + } +} + +// Checks if selected word is valid solution +static int isSolution(const char *solution, SearchWordPanel searchWordPanel) +{ + for(int i = 0; i < searchWordPanel.count; i++) + if(strcmp(solution, searchWordPanel.words[i].solution) == 0) + { + // Mark word as found and return true if solution matches + searchWordPanel.words[i].wasFound = 1; + return 1; + } + + return 0; // false if not found +} + +// Updates the marked squares based on user selection +static void updateSelectedSquares(const MouseSelection selection, CharSquarePanel squarePanel, SearchWordPanel searchWordPanel) +{ + unsigned int wordIdx = 0; + char selectedWord[MAX_WORD_LEN]; + unsigned int selectedIdx[squarePanel.count]; + float radius = squarePanel.squareSize / 2; + + // Loop through all squares and check if selected + for(int i = 0; i < squarePanel.count && wordIdx < MAX_WORD_LEN-1; i++) + { + Vector2 center = {squarePanel.squares[i].position.x + squarePanel.position.x, squarePanel.squares[i].position.y + squarePanel.position.y}; + center.x += radius; + center.y += radius; + + // Check if square is selected by mouse + if(CheckCollisionCircleLine(center, radius, selection.startPosition, selection.endPosition)) + { + selectedWord[wordIdx] = squarePanel.squares[i].character[0]; + selectedIdx[wordIdx] = i; + wordIdx++; + } + } + selectedWord[wordIdx] = '\0'; + + // If selected word is a solution, mark it + if(isSolution(selectedWord, searchWordPanel)) + { + for(int i = 0; i < wordIdx; i++) + squarePanel.squares[selectedIdx[i]].isMarked = 1; + } +} + +// Handles mouse input for selecting words in grid +static void handleMouseInput(MouseSelection *selection, CharSquarePanel squarePanel, SearchWordPanel searchWordPanel) +{ + if(IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) // Start new selection + { + selection->startPosition = GetMousePosition(); + selection->endPosition = selection->startPosition; + selection->isSelected = 1; + } + else if(IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) // End selection + { + updateSelectedSquares(*selection, squarePanel, searchWordPanel); + + selection->isSelected = 0; + } + else if(IsMouseButtonDown(MOUSE_BUTTON_LEFT)) // Update end position while selecting + { + selection->endPosition = GetMousePosition(); + } +} + +// Draws selection line on screen if selection is active +static void drawSelection(const MouseSelection selection) +{ + if(selection.isSelected) + { + DrawLine(selection.startPosition.x, selection.startPosition.y, selection.endPosition.x, selection.endPosition.y, WHITE); + } +} + +// Draws search word panel (list of words to be found) +static void drawSearchWordPanel(SearchWordPanel searchWordPanel) +{ + for(int i = 0; i < searchWordPanel.count; i++) + { + Vector2 wordScreenCoord = {searchWordPanel.words[i].position.x + searchWordPanel.position.x, searchWordPanel.words[i].position.y + searchWordPanel.position.y}; + DrawText(searchWordPanel.words[i].content, wordScreenCoord.x, wordScreenCoord.y, searchWordPanel.fontSize, WHITE); + + // If word has been found, highlight it + if(searchWordPanel.words[i].wasFound) + { + int xOffset = MeasureText(searchWordPanel.words[i].content, searchWordPanel.fontSize); + for(int i = 0; i <= 3; i++) + DrawLine(wordScreenCoord.x - 3, wordScreenCoord.y + 5 + i, wordScreenCoord.x + xOffset + 3, wordScreenCoord.y + 5 + i, GREEN); + } + } +} + +// Draws helper message (instructions or tips for user) +static void drawHelperMessage(const HelperMessage msg) +{ + DrawRectangle(msg.position.x, msg.position.y, msg.size.x, msg.size.y, BLACK); // Background for message + DrawText(msg.text, msg.position.x + 5, msg.position.y + 5, msg.fontSize, WHITE); // Display message text +} + +// Draws the entire game content, including helper message, squares, and search words +static void drawGameContent(const HelperMessage helperMsg, const CharSquarePanel squarePanel, const MouseSelection selection, const SearchWordPanel searchWordPanel) +{ + drawHelperMessage(helperMsg); + drawSquares(squarePanel); + drawSearchWordPanel(searchWordPanel); + drawSelection(selection); +} + +// Draws success message when player wins +static void drawSuccessContent(WinMessage msg) +{ + unsigned int textWidth = MeasureText(msg.content, msg.size); + DrawRectangle(msg.position.x-20, msg.position.y-20, textWidth+40, msg.size+40, GREEN); // Background for success message + + for(int i = 0; i < 5; i++) // Draw borders around success message + DrawRectangleLines(msg.position.x-20+i, msg.position.y-20+i, textWidth+40-i*2, msg.size+40-i*2, WHITE); + + DrawText(msg.content, msg.position.x, msg.position.y, msg.size, WHITE); // Display success text +} + +// Draws entire game screen, including game content and success message if applicable +static void drawAll(const CharSquarePanel squarePanel, const MouseSelection selection, const SearchWordPanel searchWordPanel, const HelperMessage helperMsg, const WinMessage msg, int hasWon) +{ + BeginDrawing(); + ClearBackground(BLACK); // Clear screen with a black background + drawGameContent(helperMsg, squarePanel, selection, searchWordPanel); // Draw game content + + if(hasWon) // If player has won, draw success message + drawSuccessContent(msg); + + EndDrawing(); +} + +// Checks if all words in the search word panel have been found +static int allWordsFound(SearchWordPanel searchWordPanel) +{ + // Loop through all words and check if any is not found + for(int i = 0; i < searchWordPanel.count; i++) + if(!searchWordPanel.words[i].wasFound) + return 0; // Return false if any word is not found + return 1; // Return true if all words are found +} + +// Main game loop where game is run and updated +static void gameLoop(const Vector2 screenSize, MouseSelection mouseSelection, CharSquarePanel squarePanel, SearchWordPanel searchWordPanel, const HelperMessage helperMsg, const WinMessage winMsg) +{ + InitWindow(screenSize.x, screenSize.y, "Word Salad"); // Initialize game window + + SetTargetFPS(60); + + while (!WindowShouldClose()) // Keep running until window is closed + { + handleMouseInput(&mouseSelection, squarePanel, searchWordPanel); // Handle mouse input (selection) + + // Draw all game content including helper message, squares, and search word panel + drawAll(squarePanel, mouseSelection, searchWordPanel, helperMsg, winMsg, allWordsFound(searchWordPanel)); + } + + CloseWindow(); +} + +// Initializes and starts game, setting up necessary elements and entering game loop +void startGame(const char wordSalad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldSize, char words[][MAX_WORD_LEN], unsigned int numberOfWords, unsigned int windowSize) +{ + const int windowWidth = windowSize; + + Vector2 screenSize; + + // Create necessary game elements like helper message, square panel, and search word panel + HelperMessage helperMsg = createHelperMessage(windowWidth); + CharSquarePanel squarePanel = createCharSquarePanel(wordSalad, searchFieldSize, windowWidth); + SearchWordPanel searchWordPanel = createSearchWordPanel(words, numberOfWords, windowWidth); + WinMessage winMsg = createWinMessage(windowWidth); + + MouseSelection mouseSelection; + + mouseSelection.isSelected = 0; // Initialize mouse selection to not be active + + // Adjust panel positions + squarePanel.position.y = helperMsg.size.y; + searchWordPanel.position.y = helperMsg.size.y + squarePanel.size.y; + + // Set screen size based on the panels' sizes + screenSize.x = squarePanel.size.x; + screenSize.y = helperMsg.size.y + squarePanel.size.y + searchWordPanel.size.y; + + // Start game loop + gameLoop(screenSize, mouseSelection, squarePanel, searchWordPanel, helperMsg, winMsg); + + // Free up allocated memory when game is done + freeCharSquarePanel(&squarePanel); + freeSearchWordPanel(&searchWordPanel); +} + +/*gcc -fPIC -c input.c game.c graphicalGame.c main.c +gcc -shared -o libwortsalat.a input.o game.o graphicalGame.o main.o*/ \ No newline at end of file diff --git a/graphicalGame.h b/graphicalGame.h new file mode 100644 index 0000000..3dbd572 --- /dev/null +++ b/graphicalGame.h @@ -0,0 +1,8 @@ +#ifndef GUI_H +#define GUI_H + +#include "game.h" + +void startGame(const char wordSalad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldSize, char words[][MAX_WORD_LEN], unsigned int numberOfWords, unsigned int windowWidth); + +#endif diff --git a/input.c b/input.c new file mode 100644 index 0000000..9652984 --- /dev/null +++ b/input.c @@ -0,0 +1,12 @@ +#include "input.h" +#include +#include + +// TODO: +// eine Funktion implementieren, die ein einzelnes Wort aus einer Textdatei (words.txt) einliest und als C-String zurückgibt. + +// Read words from file and store in 'words' array +int readWords(FILE *file, char words[][MAX_WORD_LEN], unsigned int maxWordCount) +{ +printf(' %c ' words); +} \ No newline at end of file