3 Commits
9 changed files with 775 additions and 39 deletions

No files matched your search

+75 -5
View File
@@ -1,8 +1,78 @@
# setup remote
# Wortsalat
## in VS Code
Ein Wortsuchspiel in C, entstanden im Praktikum Informatik 2 (TH Nürnberg).
Das Programm liest Wörter aus einer Textdatei, versteckt sie zufällig horizontal oder vertikal in einem 20×20-Buchstabengitter und füllt die restlichen Felder mit zufälligen Buchstaben. Ziel ist es, alle Wörter im Gitter zu finden.
## Spielablauf
1. Beim Start werden die Wörter aus der angegebenen Datei eingelesen und in Großbuchstaben umgewandelt.
2. Jedes Wort wird an einer zufälligen Position platziert, von links nach rechts oder von oben nach unten. Wörter dürfen sich kreuzen, wenn sie an der Kreuzung denselben Buchstaben haben.
3. Kann ein Wort nicht platziert werden (z. B. weil es länger als das Gitter ist), bricht das Programm mit einer Fehlermeldung ab.
4. Es öffnet sich ein Fenster mit dem Buchstabengitter, darunter steht die Liste der gesuchten Wörter.
5. Sind alle Wörter gefunden, erscheint die Meldung „Congratulations! You won!“.
## Bedienung
| Aktion | Wirkung |
|---|---|
| Linke Maustaste gedrückt halten und eine Linie genau über die Buchstaben eines Wortes ziehen | Wort auswählen |
| Maustaste loslassen | Auswahl prüfen: ein richtiges Wort wird im Gitter grün markiert und in der Liste durchgestrichen |
| `Esc` oder Fenster schließen | Spiel beenden |
| `hack` tippen (nur mit ./wordsalad_cheats) | Cheat-Modus ein- bzw. ausschalten: alle noch nicht gefundenen Wörter werden dunkelblau hinterlegt |
## Bauen und Starten
Voraussetzung ist `gcc` (unter macOS reicht das Clang aus den Xcode Command Line Tools) und `make`.
Alle Befehle werden im Ordner `Startcode/` ausgeführt; das Makefile erkennt Linux, macOS und Windows automatisch.
```bash
cd Startcode
make wordsalad
./wordsalad words.txt
```
| Target | Inhalt |
|---|---|
| `make wordsalad` | Das Spiel komplett aus dem eigenen Code |
| `make wordsalad_cheat` | Eigene `main.c`, `input.c`, `game.c` und `graphicalGame+Cheas,c`|
| `make wordsalad_initial` | Die mitgelieferte Referenzversion (`libwordsalad_complete.a`) |
| `make test` | Baut die Unit-Tests als `runTests` |
| `make clean` | Löscht Objektdateien und erzeugte Programme |
## Format der Wortdatei
Die Wörter werden durch Leerzeichen, Komma, Semikolon, Tabulator oder Zeilenumbruch getrennt. Groß- und Kleinschreibung spielt keine Rolle. Beispiel (`words.txt`):
```
git remote add origin https://git.efi.th-nuernberg.de/gitea/glockma106904/Info2-Praktikum.git
git push -u origin main
```
Yeti,Nessie Werwolf; Vampir
Monster
Hydra;Frankenstein
```
Ein Wort darf höchstens so lang sein wie das Gitter (20 Buchstaben).
## Tests
```bash
make test && ./runTests
```
Die Tests (Framework [Unity](https://github.com/ThrowTheSwitch/Unity)) prüfen das Einlesen der Wörter (`readWords`) und das Erzeugen des Gitters (`createWordSalad`).
## Projektstruktur
| Datei | Inhalt |
|---|---|
| `main.c` | Programmstart: Datei öffnen, Wörter einlesen, Gitter erzeugen, Spiel starten |
| `input.c` / `input.h` | `readWords`: Wörter aus der Datei lesen, trennen und in Großbuchstaben umwandeln |
| `game.c` / `game.h` | `createWordSalad`: Wörter zufällig platzieren und leere Felder auffüllen |
| `graphicalGame.c` / `graphicalGame.h` | Fenster, Zeichnen und Mausbedienung mit [raylib](https://www.raylib.com), inklusive Cheat-Modus |
| `unit_tests.c` | Unit-Tests |
| `words.txt` | Beispiel-Wortliste |
| `linux/`, `macos-*/`, `windows/` | Vorkompilierte Bibliotheken (raylib und Referenzlösung) je Betriebssystem |
## Repository klonen :)
```bash
git clone https://git.efi.th-nuernberg.de/gitea/glockma106904/Info2-Praktikum.git
```
+109 -5
View File
@@ -5,13 +5,117 @@
#define MAX_RAND_TRIES_PER_WORD 10
#define EMPTY_CHAR 0
#define SIMPLE_MODE 1 // keine Reverse einträge?
typedef enum
{
HORIZONTAL,
VERTICAL
} Direction;
static int canPlaceWord(char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], const char *word, unsigned int wordLen, unsigned int row, unsigned int col, Direction dir){
for(unsigned int k = 0; k < wordLen; k++){
//iteriert durch das 2D Grid je nach Richtung
unsigned int r = (dir == VERTICAL) ? row + k : row;
unsigned int c = (dir == HORIZONTAL) ? col + k: col;
// Falls das Feld leer ist oder der gleiche Buchstabe drinnen ist.
if(salad[r][c] != EMPTY_CHAR && salad[r][c] != word[k])
return 0;
}
return 1;
}
static void placeWord(char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], const char *word, unsigned int wordLen, unsigned int row, unsigned int col, Direction dir){
for(unsigned int k = 0; k < wordLen; k++){
//iteriert durch das 2D Grid je nach Richtung
unsigned int r = (dir == VERTICAL) ? row + k : row;
unsigned int c = (dir == HORIZONTAL) ? col + k : col;
//schreibt das wort
salad[r][c] = word[k];
}
}
static int tryPlaceWord(char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldLen, const char *word){
unsigned int wordLen = strlen(word);
if(wordLen == 0 || wordLen > searchFieldLen) // sichergehen, dass es ein valides Wort ist
return 0;
for(int tries = 0; tries < MAX_RAND_TRIES_PER_WORD; tries++){
Direction dir = (rand() % 2 == 0 ) ? HORIZONTAL : VERTICAL;
unsigned int searchSize;
unsigned int row, col;
/* Hatte ueberlegt hier einen schwer Modus zubauen der auch woerter rueckwaerts setzt, war dann aber zu faul.
if(SIMPLE_MODE){
searchSize = searchFieldLen - wordLen;
} else {
searchSize = searchFieldLen;
}
*/
searchSize = searchFieldLen - wordLen;
if(dir == HORIZONTAL)
{
row = rand() % searchFieldLen; // Macht das sinn? Wenn 412 % bspw. 8 = 4
col = rand() % (searchSize + 1); // Bspw. 154 % ((20 - 6) +1) = 14
}
else
{
row = rand() % (searchSize + 1);
col = rand() % searchFieldLen;
}
if(canPlaceWord(salad, word, wordLen, row, col, dir)) // schaut nach ob es platzieren kann
{
placeWord(salad, word, wordLen, row, col, dir); // platziert das wort, falls ja
return 1;
}
}
return 0;
}
// füllt restliche Leere Felder mit random Buchstaben.
static void fillEmpty(char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldLen){
for(unsigned int r = 0; r < searchFieldLen; r++){
for(unsigned int c = 0; c < searchFieldLen; c++){
if(salad[r][c] == EMPTY_CHAR){
salad[r][c] = 'A' + (rand() % 26); // start mit A also 0100 0001/Dec(65)und addiert dann 0-25 um bis Z abzudecken.
}
}
}
}
//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)
{
static int isSeeded = 0;
int placedWords = 0;
}
if (searchFieldLen > MAX_SEARCH_FIELD_LEN){
return 0;
}
// stellt sich wieder haben Random Seed
if(!isSeeded){
srand((unsigned int)time(NULL));
isSeeded = 1;
}
//setzt alle einträge im array auf 0
for(unsigned int r = 0; r < searchFieldLen; r++){
for(unsigned int c = 0; c < searchFieldLen; c++){
salad[r][c] = EMPTY_CHAR;
}
}
// Loop für alle Wörter
for(unsigned int i = 0; i < wordCount; i++){
if(tryPlaceWord(salad, searchFieldLen, words[i])){
placedWords++;
}
}
fillEmpty(salad, searchFieldLen);
return placedWords;
}
+1
View File
@@ -7,4 +7,5 @@
int createWordSalad(char salad[MAX_SEARCH_FIELD_LEN][MAX_SEARCH_FIELD_LEN], unsigned int searchFieldLen, const char words[][MAX_WORD_LEN], unsigned int wordCount);
#endif
+547
View File
@@ -0,0 +1,547 @@
#include <stdlib.h>
#include <string.h>
#include "graphicalGame.h"
#include "raylib.h"
#define MAX_MESSAGE_LEN 256
#define MAX_SOLUTION_WORD_LEN 16
#define CHEAT_CODE "hack"
#define CHEAT_COLOR CLITERAL(Color){ 0, 30, 80, 255 } // Much darker than DARKBLUE of the normal squares
static int isCheatActive = 0;
static int isCheatSquare[MAX_SEARCH_FIELD_LEN * MAX_SEARCH_FIELD_LEN]; // 1 if square belongs to a search word
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;
}
else if(isCheatActive && isCheatSquare[i])
{
squareColor = CHEAT_COLOR;
}
// 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
}
// Marks the squares of a word in the cheat array, if the word starts at the given position
static void markIfWordAt(const CharSquarePanel squarePanel, unsigned int gridSize, const char *word, unsigned int row, unsigned int col, int isVertical)
{
unsigned int wordLen = strlen(word);
unsigned int startIdx = isVertical ? row : col;
// Word would leave the grid
if(startIdx + wordLen > gridSize)
return;
// Squares are stored row by row, so square (r, c) has index r * gridSize + c
for(unsigned int k = 0; k < wordLen; k++)
{
unsigned int r = isVertical ? row + k : row;
unsigned int c = isVertical ? col : col + k;
if(squarePanel.squares[r * gridSize + c].character[0] != word[k])
return;
}
for(unsigned int k = 0; k < wordLen; k++)
{
unsigned int r = isVertical ? row + k : row;
unsigned int c = isVertical ? col : col + k;
isCheatSquare[r * gridSize + c] = 1;
}
}
// Searches all words in the grid and marks their squares in the cheat array
static void findAllWords(const CharSquarePanel squarePanel, const SearchWordPanel searchWordPanel)
{
unsigned int gridSize = 0;
// The grid is square, so count = gridSize * gridSize
while(gridSize * gridSize < squarePanel.count)
gridSize++;
memset(isCheatSquare, 0, sizeof(isCheatSquare));
for(unsigned int w = 0; w < searchWordPanel.count; w++)
{
for(unsigned int row = 0; row < gridSize; row++)
{
for(unsigned int col = 0; col < gridSize; col++)
{
markIfWordAt(squarePanel, gridSize, searchWordPanel.words[w].solution, row, col, 0);
markIfWordAt(squarePanel, gridSize, searchWordPanel.words[w].solution, row, col, 1);
}
}
}
}
// Remembers the last typed letters and toggles the cheat mode when they match the cheat code
static void handleCheatInput(const CharSquarePanel squarePanel, const SearchWordPanel searchWordPanel)
{
static char typed[sizeof(CHEAT_CODE)] = "";
const unsigned int codeLen = strlen(CHEAT_CODE);
int key = GetCharPressed();
// GetCharPressed returns one typed character per call, 0 if there are no more
while(key > 0)
{
// Shift the typed letters one to the left and append the new one
for(unsigned int i = 0; i + 1 < codeLen; i++)
typed[i] = typed[i + 1];
typed[codeLen - 1] = (char)key;
typed[codeLen] = '\0';
if(strcmp(typed, CHEAT_CODE) == 0)
{
isCheatActive = !isCheatActive;
if(isCheatActive)
findAllWords(squarePanel, searchWordPanel);
typed[0] = '\0';
}
key = GetCharPressed();
}
}
// 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)
handleCheatInput(squarePanel, searchWordPanel); // Toggle cheat mode when "hack" is typed
// 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*/
+26 -20
View File
@@ -2,33 +2,39 @@
#include <string.h>
#include <ctype.h>
// TODO:
// eine Funktion implementieren, die ein einzelnes Wort aus einer Textdatei (words.txt) einliest und als C-String zurückgibt.
#define DELIMITERS " ,;\t\r\n"
// Read words from file and store in 'words' array
int readWords(FILE *file, char words[][MAX_WORD_LEN], unsigned int maxWordCount)
{
char puffer[MAX_LINE_LEN];
char *teiler = " ,;\n";
char *token;
FILE *words_txt_speicherort; //Zeiger auf den Speicherort der word.txt Datei
unsigned int wordCount = 0;
words_txt_speicherort = fopen("words.txt","r"); //word.txt öffnen
if(words_txt_speicherort == NULL) //Abbruch falls words.txt nicht existieren würde
if(file == NULL || words == NULL)
return 0;
//Liest die Datei Line by Line
while(wordCount < maxWordCount && fgets(puffer, MAX_LINE_LEN, file) != NULL)
{
return -1;
//Trennt das erste Wort der Zeichenkette, abhängig von den Teilerbedingungen
char *token = strtok(puffer, DELIMITERS);
//geht über das token und speichert sie einzeln in Uppercase
while(token != NULL && wordCount < maxWordCount)
{
unsigned int i = 0;
while(token[i] != '\0' && i < MAX_WORD_LEN -1)
{
words[wordCount][i] = (char)toupper((unsigned char)token[i]);
i++;
}
words[wordCount][i] = '\0';
wordCount++;
token = strtok(NULL, DELIMITERS);
}
}
fgets(puffer, MAX_LINE_LEN, words_txt_speicherort); //speichert alle Zeichen der words.txt datei in den speicher
token = strtok(puffer, teiler); //Trennt das erste Wort der Zeichenkette, abhängig von den Teilerbedingungen
while(token != NULL)
words = token; //speichert nur das einzelne Word in das words array; Problem: Laufindex der global gespeichert werden müsste; wie springe ich beim 2ten Funktionsaufruf zum nächsten Wort
int fclose(words_txt_speicherort);
return wordCount;
}
+6 -4
View File
@@ -37,10 +37,12 @@ int main(int argc, char *argv[])
placedWords = createWordSalad(wordSalad, SALAD_SIZE, words, wordCount);
// TODO:
// Check if all words were successfully placed
// Start the game if successful
// error message if some words couldn't be placed
if(placedWords == wordCount){
startGame(wordSalad,SALAD_SIZE,words,wordCount,800);
} else {
fprintf(stderr, "konnte nicht alle Wörter platzieren %d von %d \n", placedWords, wordCount);
exitCode = EXIT_FAILURE;
}
}
else
{
+10 -1
View File
@@ -28,6 +28,12 @@ wordsalad_initial:
wordsalad: main.o input.o game.o graphicalGame.o $(BINARIES)/libraylib.a
$(CC) $(CFLAGS) -o wordsalad main.o input.o game.o graphicalGame.o $(BINARIES)/libraylib.a $(LDFLAGS)
wordsalad_complete: main.o input.o game.o $(BINARIES)/libwordsalad.a $(BINARIES)/libraylib.a
$(CC) $(CFLAGS) -o wordsalad_complete main.o input.o game.o $(BINARIES)/libwordsalad.a $(BINARIES)/libraylib.a $(LDFLAGS)
wordsalad_cheat: main.o input.o game.o graphicalGame+Cheats.o $(BINARIES)/libwordsalad.a $(BINARIES)/libraylib.a
$(CC) $(CFLAGS) -o wordsalad_cheat main.o input.o game.o graphicalGame+Cheats.o $(BINARIES)/libwordsalad.a $(BINARIES)/libraylib.a $(LDFLAGS)
main.o: $(codefolder)/main.c
$(CC) -c $(CFLAGS) $(codefolder)/main.c
@@ -40,6 +46,9 @@ game.o: $(codefolder)/game.c
graphicalGame.o: $(codefolder)/graphicalGame.c
$(CC) -I$(raylibfolder) -c $(CFLAGS) $(codefolder)/graphicalGame.c
graphicalGame+Cheats.o: $(codefolder)/graphicalGame+Cheats.c
$(CC) -I$(raylibfolder) -c $(CFLAGS) $(codefolder)/graphicalGame+Cheats.c
# --------------------------
# Unit Tests
@@ -55,5 +64,5 @@ clean:
ifeq ($(OS),Windows_NT)
del /f *.o *.exe
else
rm -f *.o wordsalad $(TEST_BIN)
rm -f *.o wordsalad wordsalad_myversion $(TEST_BIN)
endif
BIN
View File
Binary file not shown.
+1 -4
View File
@@ -32,12 +32,9 @@
### Requirements
* Eine .txt Datei kann eingelesen werden
* Es wird nach den Trennzeichen: , ; . : - gefiltert um zu trennen
* Es wird nach den Trennzeichen: , ; . : gefiltert um zu trennen
* Es sind nur Buchstaben erlaubt, keine Zahlen, Sonderzeichen, etc.
* Es werden beliebig viele Füller entfernt bis keine mehr enthalten sind.
* Füller sind Leading oder Trailing:
* " " (Leerzeichen)
* /n
* Füller im Wort müssen erkannt werden und Programm beenden.
* Wörter werden gleichmäßig klein ODER großgeschrieben werden und so weiterhin gespeichert werden.
* Wenn ein eingelesenes Wort länger als das Grid in Horizontal oder Vertikal ist, bricht das Programm ab