/* ** AUKKO.C ** ** ** Tekij„: Sanna Lehikoinen ** Ohjelmointikurssi -92 ** 15.5.1992 ** ** ** Yleinen tietorakenne on seuraava: ** ** ** Tehtavatyyppi ** ____ ** | | ** | | Lausetyyppi ** | 20 | Lauseet ____ ** | 2 | ___ | | ** |____| ------>| o-|----->|____| ____ ** | o-|--------------------->| | ** | o-|---->? |____| ** | o-|---->? ** |___| ** : ** ** Lausetyyppi ** ____________________________________________________ ** |6 | ** |4 | ** |----------------------------------------------------| ** |Alice was beginning to get very tired 1) sitting | ** |2) her sister 3) the bank, and 4) having | ** |nothing to do. | ** |----------------------------------------------------| ** 0|3 | ** |2 | ** |1 | ** | 0 1 2 3 4 5 | ** |----------------------------------------------------| ** |up |through |of | | | | ** |----------------------------------------------------| ** 1|3 | ** |1 | ** |2 | ** |----------------------------------------------------| ** |at |by |off | | | | ** |----------------------------------------------------| ** 2|3 | ** |1 | ** |3 | ** |----------------------------------------------------| ** |from |on |through | | | | ** 3|3 | ** |2 | ** |4 | ** |----------------------------------------------------| ** |towards|to |of | | | | ** |____________________________________________________| ** */ #include #include #include #include "mjonot.h" /***************************************************************************/ #define MAX_LAUSEITA 35 #define MAX_VAIHTOEHTOJA 6 #define MAX_VAIHTOEHD_PITUUS 15 #define MAX_LAUSEEN_PITUUS 300 #define MAX_AUKKOJA 20 #define VAIN_ISOT 1 #define TARKENNIN ".DAT" /***************************************************************************/ typedef struct { int vaihtoehtoja; int oikea_vastaus; int aukon_nro; char vaihtoehdot[MAX_VAIHTOEHTOJA][MAX_VAIHTOEHD_PITUUS]; } Aukko_tyyppi; typedef struct { int aukkoja; char lause[MAX_LAUSEEN_PITUUS]; Aukko_tyyppi aukot[MAX_AUKKOJA]; } Lause_tyyppi; typedef struct { char tehtavan_nimi[20]; char tiedoston_nimi[20]; int lauseita,max_lauseita; Lause_tyyppi **lauseet; } Tehtava_tyyppi; /***************************************************************************/ /* ** FUNKTION NIMI: LUE_LAUSE ** Funktiolla luetaan lauseita n„yt”lt„, EI tiedostosta. */ void lue_lause(Lause_tyyppi *lause) { strcpy(lause->lause,"Alice was beginning to get very tired 1) sitting 2) her sister\n" "3) the bank,and 4) having nothing to do."); lause->aukkoja = 4; lause->aukot[0].vaihtoehtoja = 3; lause->aukot[0].oikea_vastaus = 2; strcpy(lause->aukot[0].vaihtoehdot[0],"up"); strcpy(lause->aukot[0].vaihtoehdot[1],"through"); strcpy(lause->aukot[0].vaihtoehdot[2],"of"); lause->aukot[1].vaihtoehtoja = 3; lause->aukot[1].oikea_vastaus = 1; strcpy(lause->aukot[1].vaihtoehdot[0],"at"); strcpy(lause->aukot[1].vaihtoehdot[1],"by"); strcpy(lause->aukot[1].vaihtoehdot[2],"off"); lause->aukot[2].vaihtoehtoja = 3; lause->aukot[2].oikea_vastaus = 1; strcpy(lause->aukot[2].vaihtoehdot[0],"from"); strcpy(lause->aukot[2].vaihtoehdot[1],"on"); strcpy(lause->aukot[2].vaihtoehdot[2],"through"); lause->aukot[3].vaihtoehtoja = 3; lause->aukot[3].oikea_vastaus = 2; strcpy(lause->aukot[3].vaihtoehdot[0],"towards"); strcpy(lause->aukot[3].vaihtoehdot[1],"to"); strcpy(lause->aukot[3].vaihtoehdot[2],"of"); } /***************************************************************************/ /* ** FUNKTION NIMI: TULOSTA_LAUSE ** Funktiolla tulostetaan lause valittuun tiedostoon. */ void tulosta_lause(FILE *f,Lause_tyyppi *lause,int aukko) { int i; fprintf(f,"%s\n", lause->lause); for (i=0; iaukkoja; i++) { fprintf(f,"%s\n", lause->aukot[aukko].vaihtoehdot[i]); } } /***************************************************************************/ /* ** FUNKTION NIMI: LUE_MERKKI ** Funktiolla luetaan n„pp„imist”lt„ yksi merkki. */ char lue_merkki(void) { #ifdef GETCH return getch(); #else char s[50]; fgets(s,50,stdin); return s[0]; #endif } /***************************************************************************/ /* ** FUNKTION NIMI: ISOKSI ** Funktiolla muutetaan pieni kirjain isoksi. */ /*char isoksi (char c) { if ( c<'a' ) return c; if ( c>'z' ) return c; return c-(char)('a'-'A'); }*/ /***************************************************************************/ /* ** FUNKTION NIMI: ODOTA_NAPPAIN ** P„„tteelt„ luetaan merkki. Jos isot ja pienet kirjaimet halutaan ** samaistaa (isot!=0), niin muutetaan luettu merkki isoksi. ** Jos oletus ei ole 0-merkki ja ollaan kohdattu rivinvaihto ** tai RET-n„pp„imen painallus palautetaan oletus. ** T„m„ toistuu kunnes ollaan luettu merkki joka kuuluu ** sallittuun joukkoon (kelpaavat). ** Jos sallittu joukko on NULL, niin kaikki painetut kelpaavat. */ char odota_nappain(char *kelpaavat, char oletus, int isot) { char painettu; do { painettu=lue_merkki(); if (isot) painettu = isoksi(painettu); if ( oletus && ((painettu=='\n') || (painettu=='\r')) ) return oletus; } while ( kelpaavat && !strchr(kelpaavat,painettu) ); return painettu; } /***************************************************************************/ /* ** ALIOHJELMAN NIMI: ODOTA_JOTAKIN ** Aliohjelmalla odotetaan kunnes jotakin n„pp„int„ on painettu. */ void odota_jotakin(void) { printf("Paina jotakin!\n"); odota_nappain(NULL,0,VAIN_ISOT); } /***************************************************************************/ /* ** FUNKTION NIMI: KYLLA_VASTAUS ** Funktiolla odotetaan kunnes painetaan K tai E tai RET. */ int kylla_vastaus(void) { return (odota_nappain("KE",'K',VAIN_ISOT)=='K'); } /***************************************************************************/ /* ** FUNKTION NIMI: EI_TOIMI ** Funktiolla tulostetaan teksti 'ei toimi' ja odotetaan n„pp„int„. */ void ei_toimi(Tehtava_tyyppi *tehtava) { printf("\nEi toimi viel„! "); if (tehtava->lauseita==0); odota_jotakin(); } /***************************************************************************/ /* ** FUNKTION NIMI: LISAA_LAUSE ** Funktiolla lis„t„„n uusi lause teht„v„„n. */ int lisaa_lause(Tehtava_tyyppi *tehtava, Lause_tyyppi *lause) { Lause_tyyppi *uusi_lause; if ( tehtava->lauseita >= tehtava->max_lauseita) return 1; uusi_lause = malloc(sizeof(Lause_tyyppi)); if ( uusi_lause==NULL ) return 1; memcpy(uusi_lause,lause,sizeof(Lause_tyyppi)); tehtava->lauseet[tehtava->lauseita]=uusi_lause; tehtava->lauseita++; return 0; } /***************************************************************************/ /* ** FUNKTION NIMI: KYSY_NIMI ** Funktiolla kysyt„„n k„ytt„j„n nimi. */ void kysy_nimi(char *nimi,int max_koko) { printf("Anna nimesi>"); lue_jono(nimi,max_koko); printf("Hei %s! T„m„ on aukkoteht„v„.\n",nimi); } /***************************************************************************/ /* ** FUNKTION NIMI: ESIKATSELU ** Funktiolla tulostetaan aukotettu teksti n„ytt””n (ei vastausvaihtoehtoja). */ void esikatselu(Tehtava_tyyppi *tehtava) { int i; printf("\n\n"); printf("Lue alla oleva teksti ensin huolellisesti l„pi:\n"); printf("\n"); for (i=0; ilauseita; i++) { printf("%s",tehtava->lauseet[i]->lause); } odota_jotakin(); } /***************************************************************************/ /* ** ALIOHJELMAN NIMI: OHJEET ** Aliohjelmalla annetaan k„ytt„j„lle ohjeet. */ void ohjeet(void) { printf("\n\n"); printf("Nyt n„et lauseita yksi kerrallaan.\n"); printf("Yhdess„ lauseessa voi olla monta aukkoa.\n"); printf("Lauseen alla n„et vastausvaihtoehtoja. Vain yksi niist„ on oikein.\n"); printf("Sinun teht„v„si on valita niist„ se, joka on mielest„si oikein.\n"); printf("\n\n"); } /***************************************************************************/ /* ** ALIOHJELMAN NIMI: KIITOKSET ** Aliohjelmalla kiitet„„n k„ytt„j„„. */ void kiitokset(void) { printf("Kiitos k„yt”st„! Moi!\n"); } /***************************************************************************/ /* ** FUNKTION NIMI: LUO_LAUSETAULUKKO ** Funktiolla luodaan tyhj„ osoitintaulukko teht„v„n lauseisiin. ** Mik„li lausetaulukkoa ei voi luoda, palautetaan virheilmoitus. ** Mik„li taulukko saadaan luotua, alustetaan se NULL-osoittimilla. */ char *EI_VOI_LUODA = "Lauseille ei saada varattua tilaa muistista!;"; char *luo_lausetaulukko(Tehtava_tyyppi *tehtava, int koko) { tehtava->lauseet = malloc( koko * (sizeof(Lause_tyyppi *)) ); tehtava->lauseita = 0; if ( tehtava->lauseet == NULL ) return EI_VOI_LUODA; return NULL; } /***************************************************************************/ /* ** FUNKTION NIMI: ALUSTA_LAUSE ** Funktiolla nollataan uusi lause, kun edellinen lause on luettu. */ void alusta_lause(Lause_tyyppi *lause) { lause->lause[0] = 0; lause->aukkoja = 0; } /***************************************************************************/ /* ** FUNKTION NIMI: LISAA_AUKKO ** Funktiolla luetaan vaihtoehtoja ja lis„t„„n ne aukkoihin. ** Lis„ksi poistetaan aukon edess„ oleva t„hti, aukon j„rjestysnumero, ** oikean vaihtoehdon edess„ oleva # ja ylim„„r„iset v„lily”nnit (ESIM.: *1. up | through | #of -> up|through|of) ** sek„ "napataan" oikea vaihtoehto. */ int lisaa_aukko(Lause_tyyppi *lause, char *jono) { int ve = 0; char *st; int j; st = palanen(jono,".",&j); sscanf(st,"%d",&(lause->aukot[lause->aukkoja].aukon_nro)); while (j>= 0){ st = palanen(NULL,"|",&j); poista_tyhjat(st); if (st[0] == '#'){ lause->aukot[lause->aukkoja].oikea_vastaus = ve; st++; } kopioi_jono(N_S(lause->aukot[lause->aukkoja].vaihtoehdot[ve]),st); ve++; /* kopioidaan luettu vaihtoehto (st) aukkoon */ /* ja kasvatetaan vaihtoehtojen lukum„„r„„ */ } lause->aukot[lause->aukkoja].vaihtoehtoja = ve; lause->aukkoja++; /* kasvatetaan aukkojen lukum„„r„„ */ return 0; } /***************************************************************************/ /* ** FUNKTION NIMI: LUE_TEHTAVA ** Funktiolla luetaan teht„v„n nimi ja tiedosto. ** ** TIEDOSTON MUOTO OLETETAAN SEURAAVAKSI: ** Teht„v„n nimi ** max_lauseita ** 1. lause aukkoineen, ESIM.: Alice was beginning to get very tired 1) sitting 2) her sister 3) the bank, and 4) of having nothing to do. ** tyhj„ rivi ** vaihtoehdot 1. lauseen aukkoihin, ESIM.: *1. up | through | #of (oikea vastaus merkit„„n risuaidalla) *2. at | #by | off *3. from | #on | through *4. towards | to | of ** tyhj„ rivi ** 2. lause aukkoineen ** tyhj„ rivi ** vaihtoehdot 2. lauseen aukkoihin ** jne. niin kauan kuin lauseita riitt„„ ** */ static char *TIEDOSTO_VAARIN = "Tiedosto on v„„r„„ muotoa!"; static char *NIMEA_EI_ANNETTU = "Harjoituksen nime„ ei annettu!"; char *lue_tehtava(Tehtava_tyyppi *tehtava) { #define PALAUTA(sanoma) {paluu_viesti = sanoma; goto sulje;} FILE *f; char jono[400], *viesti, *paluu_viesti = NULL; Lause_tyyppi lause; do { printf("Anna teht„v„n nimi>"); if(lue_jono(N_S(tehtava->tiedoston_nimi)) <= OLETUS) return NIMEA_EI_ANNETTU; kopioi_jono(N_S(jono),tehtava->tiedoston_nimi); liita_jono (N_S(jono),TARKENNIN); if (!(f=fopen(jono,"rt")) ) { printf("Kirjoita teht„v„n nimi oikein\n"); } } while (!f); if ( f_lue_jono(f,N_S(tehtava->tehtavan_nimi))max_lauseita); if ( (viesti=luo_lausetaulukko(tehtava,tehtava->max_lauseita))!=NULL ) PALAUTA(viesti); while ( !feof(f) ) { alusta_lause(&lause); if ( f_lue_jono(f,N_S(jono))<=OLETUS ) continue; if ( jono[0]==';' ) continue; do{ liita_jono(lause.lause,MAX_LAUSEEN_PITUUS,jono); liita_jono(lause.lause,MAX_LAUSEEN_PITUUS,"\n"); if ( f_lue_jono(f,N_S(jono))aukot[a].oikea_vastaus; printf("Vastauksesi kohtaan %d>", lause->aukot[a].aukon_nro); lue_jono(vastaus,30); while (strcmp(vastaus,lause->aukot[a].vaihtoehdot[oikea])!=0){ printf("V„„rin. Yrit„ uudestaan>"); lue_jono(vastaus,30); ok = 0; } printf("Oikein.\n"); printf("\n\n"); return ok; } /***************************************************************************/ /* ** FUNKTION_NIMI: SIJOITA_OIKEA ** Funktiolla sijoitetaan oikea vaihtoehto oikealle kohdalle lauseeseen. */ int sijoita_oikea(Lause_tyyppi *lause,int a) { int paikka,alku; char *kohta; char apu[300]; char etsittava[4]; sprintf(etsittava,"%d)",lause->aukot[a].aukon_nro); kohta = strstr(lause->lause,etsittava); if (kohta==NULL) return 1; paikka = kohta - lause->lause; alku = paikka-1; while ( alku >= 0 && lause->lause[alku] == ' ' ) alku--; kopioi_jono(apu,alku+1+1,lause->lause); /* +1 v„lily”nti„ varten , */ /* +1 0:n tilaa varten */ if ( (alku+1 > 0) && (lause->lause[alku] != '\n') ) liita_jono(N_S(apu)," "); /* Rivin alkuun ei " " */ liita_jono(apu,sizeof(apu),lause->aukot[a].vaihtoehdot[lause->aukot[a]. oikea_vastaus]); paikka = paikka+strlen(etsittava); while (lause->lause[paikka] == ' ' ) paikka++; liita_jono(N_S(apu)," "); liita_jono(apu,sizeof(apu),lause->lause+paikka); kopioi_jono(N_S(lause->lause),apu); return 0; } /***************************************************************************/ /* ** FUNKTION NIMI: HARJOITTELE ** Funktiolla tulostetaan ensin koko teksti aukkoineen (ei vaihtoehtoja). ** Sitten teksti„ k„yd„„n l„pi lause kerrallaan (vaihtoehdot tulostuvat ** lauseen alle) ja kysyt„„n k„ytt„j„lt„ vastausta kuhunkin aukkoon niin ** kauan kunnes h„n antaa oikean vastauksen. Lopuksi lause tulostetaan ** niin, ett„ oikea vaihtoehto on oikealla paikallaan lauseessa. ** Kun kaikki lauseet on k„yty l„pi, tulostetaan kuinka monta lausetta ** k„ytt„j„ sai oikein ensimm„isell„ yritt„misell„. */ void harjoittele(Tehtava_tyyppi *tehtava,char *nimi) { int i; int a; int oikeita = 0; int aukkoja = 0; esikatselu(tehtava); ohjeet(); for (i=0; ilauseita; i++) { for (a=0; alauseet[i]->aukkoja; a++) { tulosta_lause(stdout,tehtava->lauseet[i],a); oikeita+=kysy_vastaus(tehtava->lauseet[i],a); sijoita_oikea(tehtava->lauseet[i],a); aukkoja++; } printf("%s\n\n",tehtava->lauseet[i]->lause); } printf("Teht„v„ on lopussa.\n"); printf("\n"); printf("Tuloksesi %s:\n",nimi); printf(" Sait %d oikein %d:sta ensimm„isell„ yrityksell„.\n", oikeita,aukkoja); } /***************************************************************************/ /* ** FUNKTION NIMI: VAPAUTA_TEHTAVA ** Funktiolla vapautetaan edellisen teht„v„n varaamat muistipaikat. */ void vapauta_tehtava(Tehtava_tyyppi *tehtava) { int i; for (i=0; i<=tehtava->lauseita; i++) { free(tehtava->lauseet[i]); } free(tehtava->lauseet); } int main(void) { Tehtava_tyyppi tehtava; char nimi[30]; kysy_nimi(N_S(nimi)); do{ if (lue_tehtava(&tehtava)) return 1; harjoittele(&tehtava,nimi); printf("\n"); printf("Haluatko jatkaa toisella tehtav„ll„ (K tai E)?>"); vapauta_tehtava(&tehtava); } while(kylla_vastaus()); kiitokset(); return 0; }