IC2DA
S.B. Bosch C Programmeren
Programma-dossier WordMind Het ontwerpen en programmeren van het programma WordMind.
Ontvangen probleemstelling Voorwoord Er werd van ons verwacht een programma te ontwikkelen in ANSI-C dat het spelletje WordMind nabootst. Dit programma moet gebruik maken van de module WordStore waarvan de header file gegeven is. De module moet intern gebruik maken van een cylische dubbel gelinkte lijst. Ook de ander leerstof die aan bod is gekomen in de les komt terug in de functies, zoals functies die gebruik maken van call by value en call by reference, pointers, dynamisch geheugen vrij maken en geheugen weer vrijgeven.
Opgave Voor het uitwerken van de oefening was ik verplicht me te houden aan de opgegeven randvoorwaarden. De header file van WordStore.h moest precies op deze manier geimplementeerd worden en de beschrijving van de eindopdracht moest gevolgd worden.
Voorbeeld Geheime woord: 'brommer' The secret word: ....... Guess the word : fietsen The secret word: -----eGuess the word : branden The secret word: br---en Guess the word : boomtor The secret word: b-om-—r Guess the word : brommer --------nieuw scherm------Word “brommer” found! Another game (Y/N)? --------nieuw scherm------Your average is 5.00 Press any key to exit...
Werkplan Programmaspecificaties Het programma moet op de volgende manier aangeroepen worden: WORDMIND
Hierbij is de naam van een file met de te raden woorden. De file is een gewone text-file met op elke regel precies een woord. Het spel gaat als volgt: De computer toont op het scherm een serie puntjes, waarvan elk puntje een letter van het te raden woord voorstelt. De gebruiker mag nu een woord intoetsen ter lengte van het aantal puntjes. Vervolgens geeft de computer onder het ingetypte woord het resultaat weer volgens dit algoritme: 1. Onder elke letter die precies overeenkomt met het te raden woord (dus zowel de letter als de positie zijn goed) wordt die letter herhaald. Als alle letters goed zijn is het woord geraden, anders gaan we door als volg. Noem de niet geraden letters van het te raden woord de “restverzameling”. 2. Doe nu van links naar rechts voor alle niet geraden letters het volgende: Als de betreffende letter in de restverzameling voorkomt, zet dan onder die letter een sterretje (*) en haal die letter weg uit de restverzameling, anders zet onder die letter een streepje (-). Als het woord geraden is, is het spel afgelopen, anders mag de gebruiker nogmaals raden. In plaats van een woord in te toetsen, mag de gebruiker ook twee uitroeptekens intoetsen (!!); daarmee wordt dit spel afgebroken, de computer verklapt de oplossing en er worden als straf 10 beuren bij het reeds verbruikte aantal beurten opgeteld. Na afloop van een spel wordt er gevraagd of de gebruiker nog een spel wil spelen: Another game (Y/N)? waarna de gebruiker al dan niet een volgend spel kan spelen. Als alle woorden uit de file een keer aan de beurt zijn geweest, dan stopt het spel ook. Aan het eind van WordMind wordt een score getoond: Your average is .. De score geeft het gemiddelde aantal benodigde beurten per spel weer en wordt berekend door (voor alle spellen) het totaal aantal ingetypte woorden te delen door het totaal aantal gespeelde spellen. De score wordt in twee decimalen nauwkeurig weergegeven. De woorden uit de text-file worden in random volgorde getoond. Elk woord wordt slechts eenmaal getoond. De implementatie maakt gebruik van twee modules: 1. De ene module is een algemene module voor de opslag van woorden, WordStore genaamd. Deze module is algemeen bruikbaar voor programma's die een opslag voor woorden nodig hebben. De woorden worden in WordStore opgeslagen en kunnen van daaruit worden geraadpleegd. Voor een specificatie van de interface van WordStore zie de bijlage (interface header file WordStore.h). Implementatie-eisen: WordStore moet geimplementeerd worden middels een gestrekte rond-gelinkte lijst met dubbele links. Er is bovendien een (buiten de module niet zichtbare) pointer die naar een van de woorden in de lijst wijst. De posiite van deze pointer komt overeen van wat buiten de module bekend staat als “Current Word”. De module WordStore heeft geen enkele directe eindgebruikersinterface (maakt geen gebruik van toetsenbord en/of beeldscherm). 2. De andere module is het eigenlijke spel WordMind, dat van de module WordStore gebruik maakt voor de opslag van woorden. WordMind handelt de eindgebruikersinterface af en verzorgt het spelen van het spel.
Toepassingslayout Het programma begint met een regel waarin staat dat de gebruiker het woord kan laten zien door twee uitroeptekens in te toetsen, maar dat daar wel 10 extra beurten voor gerekend wordt: !! to reveal the word (10 extra turns added) Daarna komt een lege regel gevolgd door de tekst: The secret word: ....... Het aantal puntjes geeft aan uit hoeveel letters het geheime woord bestaat. De gebruiker krijgt daarna de mogelijkheid om het woord te raden of om twee uitroeptekens in te toetsen. De regel ziet er zo uit: Guess the word : De invoer wordt gecontroleerd en de laatste twee stappen worden opnieuw getoond, maar nu zijn de puntjes veranderd in het restverzameling. Als de gebruiker in plaats van het te raden woord twee uitroeptekens invoert dan wordt het geheime woord getoond en het spel wordt beindigd: The secret word: “....” Normaal gesproken wordt naar een aantal pogingen het woord geraden, de gebruiker krijgt dan te zien: Found the secret word: “...” Hierna krijgt de gebruiker, als er nog woorden in de lijst staan, de vraag: Another game (Y/N)? De gebruiker kan ervoor kiezen om het spel verder te spelen of om het programma te beindigen. Als het spel beindigen gekozen wordt dan wordt er de volgende regel getoond, met daarin het gemiddelde aantal beurten per spel: Your average is x.xx Press any key to exit... Wordt er gekozen voor de optie om het spel verder te spelen, dan begint het spel weer bij de eerste regel met de uitroeptekens.
Stroomschema
Init deel hoofdprogramma
Hoofdprogramma
Verdere informatie Het programma werd gecodeerd in Borland C++ Builder 5.0 en 6.0 als een console application. De werking van het programma werd getest aan de hand van een text bestand geleverd door de docent. Daarna werd het programma beoordeeld op gebruiksvriendelijkheid door een aantal vrijwilligers die niets van de opdracht afwisten. Dit heeft geleid tot verder verfijning van de applicatie.
Programmastructuur Main() Lokale variabelen: • char * cptrGuessedWord: bevat de string die de gebruiker in het spel invoerd • char * cptrSecretWord: wordt gevuld met een random woord uit het opgegeven text bestand. Na ieder spel wordt deze uit het geheugen vrij gegeven. • char * cptrResult: het aantal letters uit cptrSecretWord wordt in deze variabele herhaald als een punt, daarna wordt deze gevuld als “restverzameling” van het geraden woord cptrGuessedWord. • int iTimesPlayed: ieder keer als de gebruiker een spel speelt wordt deze variabele met een opgehoogd. • int iNrOfGuessedWords: als een gebruiker een woord raad wordt deze variabele met een opgehoogd. Deze variabele wordt bij het verlaten van het spel gebruikt samen met de variabele iTimesPlayed om het gemiddelde aantal beurten per spel te berekenen. • int iCounter: elke keer als er een spel wordt opgestart moet cptr_Result gevuld worden met puntjes, deze variabele houdt de loop bij, beginnend op positie 0. • char cPlay: aan het eind van ieder spel krijgt de gebruiker de vraag om een character in te toetsen (Y/N). Deze variabele wordt gevuld met deze keuze. • Boolean bGuessed: binnen ieder spel moet er net zo lang doorgegaan worden tot de gebruiker het woord raad of als de gebruiker de optie (!!) kiest. Deze variabele wordt TRUE als een van deze gevallen zich voordoet. • Boolean bPlay: zolang deze de waarde TRUE heeft en er nog woorden in de lijst staan blijft het spel doorgaan. Bedoeling van de functie: De main-functie onderhoudt de hoofdvariabelen en voert de spelstructuur uit. Daarnaast houdt het de statistieken bij van de gespeelde spellen. Hieruit wordt het spel geladen en uitgevoerd.
void CompareWords(char *sSecretWord, char *sGuessedWord, char *sResult) Doorgegeven variabelen: • char * sSecretWord: een string (character array met variabele grootte eindigend op '\0') met daarin het te raden woord. • char * sGuessedWord: het door de gebruiker geraden woord, met dezelfde grootte als sSecretWord. • char * sResult: na de functie bevat deze variabele de restverzameling of hetzelfde woord. Lokale variabelen: • int iLength: de lengte van het geraden woord sGuessedWord. • int iLoop: wordt gebruikt om over het woord te lopen. • int iFound: als een character gevonden is het woord wordt de positie opgeslagen in deze variabele. Dit is handiger en sneller om het in een variabele te doen, zodat dezelfde functie niet meerdere keren aangeroepen hoeft te worden. • char * cptrWorkI: lokale kopie van sGuessedWord waarmee gewerkt wordt. • char * cptrWorkV: lokale kopie van sSecretWord waarmee gewerkt wordt. Doel van de functie: Deze functie vergelijkt de string sSecretWord met sGuessedWord. Als de waarde en de positie overeenkomen wordt de letter doorgegeven in sResult. Als de waarde voorkomt maar de positie is niet goed wordt er een sterrretje (*) doorgegeven op de overeenkomende positie met sGuessedWord. Voor de overgebleven letters wordt een min (-) teken doorgegeven in sResult.
int pos( char * sSearchWord, char c, int iFromPosition ) Doorgegeven variabelen: • char * sSearchWord: het woord waarin het opgegeven karakter gevonden moet worden. • char c: bevat een letter die gezocht wordt in sSearchWord. • int iFromPosition: geeft aan op welke positie de functie moet beginnen met zoeken. Lokale variabelen: • int iReturn: de te retourneren waarde, aanvankelijk -1 maar als er een karakter gevonden wordt dan krijgt deze als waarde de positie in de string. • int iCounter: interne teller voor de loop, krijgt bij het begin de waarde van iFromPosition. Doel van de functie: Zoekt in sSearchWord naar karakter c vanaf de opgegeven positie iFromPosition. Als het opgegeven karakter niet gevonden is, geeft deze functie de waarde -1 terug.
void strtolower( char *s ) Doorgegeven variabelen: • char * s: een string die omgezet omgezet moet worden naar lower-case. Lokale variabelen: • int iLength: bevat de lengte van s • int iLoop: wordt gebruikt om over het woord te lopen. Bedoeling van de functie: Deze functie veranderd de opgegeven string in een lower-case string.
Programmacode /*****************************************************************************/ /** **/ /** File: WORDMIND.C **/ /** Author: S.B. Bosch **/ /** Version: 1.0 **/ /** Date: 2004-10-16 (YYYY-MM-DD) **/ /** **/ /** Pre: **/ /** Post: main program for the game WordMind **/ /** usage: wordmind **/ /** the is a ASCII-file containing one word **/ /** on each line. The maximum length of a word is set **/ /** to the length specified in WORDSTORE.H **/ /** **/ /** Modified: **/ /** [YYYY-MM-DD] - <description> **/ /** [2004-10-17] S.B. Bosch - added **/ /** void CompareWords(char *, char *, char *) **/ /** int pos(char *, char, int) **/ /** void strtolower(char *) **/ /** [2004-10-20] S.B. Bosch - changed the prompt for the user, now the **/ /** user can no longer type a word longer then **/ /** the current secret word. The normal reveal **/ /** option still exists (!!) **/ /** **/ /*****************************************************************************/ #include <string.h> #include <stdlib.h> #include <stdio.h> #include #include "WordStore.h" void CompareWords( char *sSecretWord, char *sGuessedWord, char *sResult); /* ** This function compares the strings sSecretWord and sGuessedWord. ** If both the value and the position of a letter are correct, the letter is ** echoed in sResult at the correct position. If the vale is correct, but ** the position is not, then an asterisk (*) is echoed at the ** corresponding position in sGuessedWord. For all remaining letters a ** hyphen (-) is echoed in sResult. ** pre: Length (sSecretWord) == Length (sGuessedWord) and sResult points to ** sufficient memory space to accomodate a string of at least the length ** of sSecretWord. ** function pos( char*, char, int) is used in this function ** post: Length (sResult) == Length (sSecretWord) and the contents of sResult ** reflect the match between sSecretWord and sGuessedWord ** Examples: ** sSecretWord: taart sSecretWord: taart ** sGuessedWord: staat sGuessedWord: torro ** sResult: -*a*t sResult: t-*r** ** sSecretWord: taart sSecretWord: taart ** sGuessedWord: arret sGuessedWord: tarot ** sResult: **--t sResult: ta*-t */ int pos( char *sSearchWord, char c, int iFromPosition ); /* ** This function searches in sSearchWord for the character c from the ** given position iFromPosition. Usage: ** iPos = pos( sSearchWord, 'a', 2 ); ** pre: sSearchWord < iFromPosition and is a valid string ** post: If the given character c is found a positive number is ** returned. If the character is not found this function returns -1 */ void strtolower( char *s ); /* ** This function converts a given string to a lowercase string */ main( int argc, char *argv[] ) {
char char char char char
*cptrGuessedWord; *cptrSecretWord; *cptrResult; sSearch[] = "abcdefghijklmnopqrstuvwxyz!"; cPlay;
int iTimesPlayed = 0; int iNrOfGuessedWords = 0; int iLengthOfSecretWord = 0; int iCounter; Boolean bGuessed, bPlay; if ( argc != 2 ) { clrscr(); printf("Usage: %s \n", argv[0] ); printf("\nPress any key to exit..."); getch(); return (0); } /* try to read the file (argv[1]) */ if (!WordStore_FileRead(argv[1])) { clrscr(); printf("Problem reading file <%s>\n", argv[1]); printf("\nPress any key to exit..."); getch(); return (0); } if ( (cptrGuessedWord = (char *) malloc(MAXLEN_WORD)) == NULL || (cptrSecretWord = (char *) malloc(MAXLEN_WORD)) == NULL || (cptrResult = (char *) malloc(MAXLEN_WORD)) == NULL ) { clrscr(); puts("Problem with allocating memory!"); printf("\nPress any key to exit..."); getch(); return (0); } /* main program */ bPlay = TRUE; while ( WordStore_NrWords() > 0 && bPlay ) { /* game part */ WordStore_MoveRandom(); iTimesPlayed++; cptrSecretWord = WordStore_WordRead(); strtolower(cptrSecretWord); /* lowercase secretword */ strcpy( cptrResult, cptrSecretWord ); iLengthOfSecretWord = strlen( cptrSecretWord ); bGuessed = FALSE; clrscr(); printf("!! to reveal the word (10 extra turns added)"); /* First time display dots */ for ( iCounter=0; iCounter
getch(); } else { printf("\nLength is not equal!\n"); puts("Please try again or type '!!' to stop this round"); }
} else { iNrOfGuessedWords++; CompareWords(cptrSecretWord, cptrGuessedWord, cptrResult ); if ( strcmp( cptrGuessedWord, cptrSecretWord ) == 0 ) { printf("\n\nFound the secret word: \"%s\"\n", cptrSecretWord ); bGuessed = TRUE; getch(); } }
} cptrSecretWord = WordStore_WordDelete(); free( cptrSecretWord ); /* end game part */
if ( WordStore_NrWords() == 0 ) { clrscr(); printf("No more words left...\nThank you for playing WordMind!\n"); getch(); bPlay = FALSE; } else { cPlay = '\0'; while ( cPlay != 'Y' && cPlay != 'N' ) { clrscr(); printf("Another game (Y/N)? "); fflush(stdin); cPlay = toupper(getch()); } if ( cPlay == 'N' ) { bPlay = FALSE; } } } /* end main program */
}
clrscr(); printf("Your average is %3.2f\n", ((float) iNrOfGuessedWords / iTimesPlayed )); printf("\nPress any key to exit..."); getch(); clrscr(); WordStore_Clear(); return (0);
void CompareWords( char *sV, char *sI, char *sO) { int iLength = strlen( sI ); int iLoop, iFound; char *cptrWorkI; char *cptrWorkV; /* Allocate memory */ if ( ( cptrWorkI = (char *) malloc(iLength+1)) == NULL || ( cptrWorkV = (char *) malloc(iLength+1)) == NULL ) { clrscr(); puts("Problem with allocating memmory!"); puts("Function: CompareWords()"); printf("\nPress any key to exit..."); fflush(stdin); getch(); exit(0); } /* Make copies of sI and sV, cptrWorkI and cptrWorkV ** must have sufficient memory space */ strcpy(cptrWorkI, sI); strcpy(cptrWorkV, sV); /* loop */ for ( iLoop = 0; iLoop < iLength; iLoop++ ) { if ( * (sI+iLoop) == * (sV +iLoop) ) { sO[iLoop] = * (sI + iLoop ); /* the same as *(s0+iLoop) */ * (cptrWorkI+iLoop) = ' '; /* space, character is found */ * (cptrWorkV+iLoop) = '.'; /* dot, charater is found */ } } /* loop again */
}
for ( iLoop = 0; iLoop < iLength; iLoop++ ) { if ( * (cptrWorkI+iLoop) != ' ' ) { iFound = pos(cptrWorkV, * (cptrWorkI+iLoop), 0); if ( iFound >= 0 ) { sO[iLoop] = '*'; * (cptrWorkI+iLoop) = ' '; /* blank out the found character */ * (cptrWorkV+iFound) = '.'; } else { sO[iLoop] = '-'; /* character not found */ } } } sO[iLoop] = '\0'; /* free memory */ free( cptrWorkI ); free( cptrWorkV );
int pos( char *s, char c, int x ) { int iReturn = -1; int iCounter; for ( iCounter = x; *(s+iCounter) != '\0'; iCounter++ ) { if ( *(s+iCounter) == c ) { iReturn = iCounter; return( iReturn ); } } return( iReturn ); } void strtolower( char *s ) { int iLength, iLoop; iLength = strlen( s ); for (iLoop=0; iLoop