Bot za tehničku podršku

Napisati program koji simulira jednostavnog bota za tehničku podršku. Bot treba da postavlja da/ne pitanja korisniku i na osnovu odgovora donosi odluke o daljim pitanjima ili rešenju problema.

Pitanja i odluke treba da budu definisane u obliku binarnog stabla, gde svaki čvor predstavlja pitanje, a grane predstavljaju odgovore da ili ne. Listovi stabla predstavljaju konačne odgovore ili rešenja problema.

Ulaz

Iz fajla stablo_odluke.txt se učitava opis binarnog stabla u prefiksnoj notaciji. Svaka linija maksimalne dužine $255$ iz fajla predstavlja jedan čvor stabla i zadaje se na sledeći način:

Bot postavlja pitanja i sa standardnog ulaza prima odgovore da ili ne (tekst maksimalne dužine $16$).

Izlaz

Bot treba da ispisuje pitanja na standardni izlaz i čeka odgovore korisnika. Ukoliko korisnik unese nešto drugo osim da ili ne, bot obaveštava korisnika o pravilu korišćenja tako što ispisuje Molim vas unesite 'da' ili 'ne'. i ponovo postavlja isto pitanje. Na kraju, kada dođe do rešenja, bot treba da ispiše rešenje.

Primer

stablo_odluke.txt

Q: Da li je računar uključen?
A: Uključite računar.
Q: Da li je ekran crn?
Q: Da li je računar spor?
A: Kontaktirajte tehničku podršku.
A: Restartujte računar.
A: Proverite kablove i priključke.

stdin

da
ne
da

stdout

Da li je računar uključen?
Da li je ekran crn?
Da li je računar spor?
Restartujte računar.

Objašnjenje

Pregled toka programa:

Da li je računar uključen?
da
Da li je ekran crn?
ne
Da li je računar spor?
da
Restartujte računar.

Primer

stablo_odluke.txt

Q: Da li je internet veza aktivna?
A: Proverite ruter.
Q: Da li su svi kablovi pravilno povezani?
A: Povežite kablove ispravno.
A: Restartujte ruter.

stdin

neee
ne

stdout

Da li je internet veza aktivna?
Molimo unesite 'da' ili 'ne'.
Da li je internet veza aktivna?
Proverite ruter.

Objašnjenje

Pregled toka programa:

Da li je internet veza aktivna?
neee
Molimo unesite 'da' ili 'ne'.
Da li je internet veza aktivna?
ne
Proverite ruter.

Primer

Rešenje

stablo_genericko.h

#ifndef __STABLO_GENERICKO_H__
#define __STABLO_GENERICKO_H__

#include <stdio.h>
#include <stdlib.h>
#include "stablo_podatak.h"

typedef enum
{
  OK,
  NEDOVOLJNO_MEMORIJE,
  NEMA_ELEMENATA
} status;

typedef struct cvor_stabla
{
  Data podatak;
  struct cvor_stabla *levo;
  struct cvor_stabla *desno;
} cvor_stabla;

typedef cvor_stabla *Stablo;

status inicijalizuj_stablo(Stablo *s);
status dodaj_u_stablo(Stablo *s, const Data *podatak,
                      int (*poredi)(const Data *, const Data *));
status pronadji(Stablo s, const Data *trazeno, Data **nadjeno,
                int (*poredi)(const Data *, const Data *));
int broj_elemenata(Stablo s);
void ispisi_stablo(Stablo s);
void obrisi_stablo(Stablo *s);

#endif

stablo_podatak.h

#ifndef __STABLO_PODATAK_H
#define __STABLO_PODATAK_H

typedef enum {
    QUESTION,
    ANSWER
} DataType;

typedef struct {
    DataType type;
    char *text;
} Data;

Data napravi_pitanje(const char* pitanje);
Data napravi_odgovor(const char* odgovor);

void dodeli(Data *dest, const Data *str);

void ispisi_podatak(const Data* data);

#endif

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "stablo_podatak.h"
#include "stablo_genericko.h"

#define FILENAME "stablo_odluke.txt"
#define MAX_LINE_SIZE 256
#define MAX_RESPONSE_SIZE 16

static status ucitaj_stablo(FILE *f, Stablo *bot)
{
	char type;
	fscanf(f, "%c%*c%*c", &type);
	char line[MAX_LINE_SIZE];

	if (type == 'Q') {
		fgets(line, MAX_LINE_SIZE, f);
		Data pitanje = napravi_pitanje(line);
		status s = dodaj_u_stablo(bot, &pitanje, NULL);
		if (s != OK) {
			return s;
		}

		s = ucitaj_stablo(f, &((*bot)->levo));
		if (s != OK) {
			return s;
		}

		s = ucitaj_stablo(f, &((*bot)->desno));
		if (s != OK) {
			return s;
		}
	} else if (type == 'A') {
		fgets(line, MAX_LINE_SIZE, f);
		Data odgovor = napravi_odgovor(line);
		status s = dodaj_u_stablo(bot, &odgovor, NULL);
		if (s != OK) {
			return s;
		}
	} else {
		return NEMA_ELEMENATA;
	}

	return OK;
}

static void interaktivni_rezim(Stablo bot)
{
	char odgovor[MAX_RESPONSE_SIZE];
	Stablo trenutni = bot;
	while (trenutni != NULL) {
		ispisi_podatak(&(trenutni->podatak));
		if (trenutni->podatak.type == ANSWER) {
			break;
		}

		scanf("%s", odgovor);

		if (strcmp(odgovor, "da") == 0) {
			trenutni = trenutni->desno;
		} else if (strcmp(odgovor, "ne") == 0) {
			trenutni = trenutni->levo;
		} else {
			printf("Molimo unesite 'da' ili 'ne'.\n");
		}
	}
}

int main(void)
{
	Stablo bot;
	status s = inicijalizuj_stablo(&bot);
	if (s != OK) {
		exit(EXIT_FAILURE);
	}

	FILE* f = fopen(FILENAME, "r");
	if (f == NULL) {
		obrisi_stablo(&bot);
		exit(EXIT_FAILURE);
	}

	s = ucitaj_stablo(f, &bot);
	if (s != OK) {
		fclose(f);
		obrisi_stablo(&bot);
		exit(EXIT_FAILURE);
	}

	interaktivni_rezim(bot);

	fclose(f);

	obrisi_stablo(&bot);

	exit(EXIT_SUCCESS);
}

stablo_genericko.c

#include "stablo_genericko.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

cvor_stabla *novi_cvor_stabla(const Data *podatak)
{
  cvor_stabla *novi = malloc(sizeof(cvor_stabla));
  if (novi == NULL)
    return NULL;
  dodeli(&(novi->podatak), podatak);
  novi->levo = NULL;
  novi->desno = NULL;
  return novi;
}

status inicijalizuj_stablo(Stablo* s) 
{
  *s = NULL;
  return OK;
}

status dodaj_u_stablo(Stablo* s, const Data* podatak, 
                      int (*poredi)(const Data*, const Data*))
{
  cvor_stabla* tmp;
  cvor_stabla* novi = novi_cvor_stabla(podatak);
  if (novi == NULL)
    return NEDOVOLJNO_MEMORIJE;
  novi->levo = NULL;
  novi->desno = NULL;

  if (*s == NULL) { 
    *s = novi;
    return OK;
  }
  tmp = *s;
  while (1) {
    if (poredi(podatak, &(tmp->podatak)) <= 0) {
      if (tmp->levo == NULL) {
          tmp->levo = novi;
        return OK;
      }
      else
        tmp = tmp->levo;
    }
    else {
      if (tmp->desno == NULL) {
          tmp->desno = novi;
        return OK;
      }
      else
        tmp = tmp->desno;
    }
  }
  return OK;
}

status pronadji(Stablo s, const Data* trazeno, Data** nadjeno, 
                int (*poredi)(const Data*, const Data*))
{
  if (s == NULL)
    return NEMA_ELEMENATA;
  if (poredi(trazeno, &(s->podatak)) == 0) {
    *nadjeno = &(s->podatak);
    return OK;
  }
  if (poredi(trazeno, &(s->podatak)) < 0) 
    return pronadji(s->levo, trazeno, nadjeno, poredi);
  return pronadji(s->desno, trazeno, nadjeno, poredi);
}

int broj_elemenata(Stablo s)
{
  return s == NULL ? 
           0 : 
           broj_elemenata(s->levo)+1+broj_elemenata(s->desno);
}

void ispisi_stablo(Stablo s) 
{
  if (s != NULL) {
     ispisi_stablo(s->levo);      
     ispisi_podatak(&(s->podatak)); 
     ispisi_stablo(s->desno);    
  }
}

void obrisi_stablo(Stablo* s) 
{
  if (*s != NULL) {
    obrisi_stablo(&((*s)->levo));
    obrisi_stablo(&((*s)->desno));
    free(*s);
    *s = NULL;
  }
}

stablo_podatak.c

#include <stdio.h>
#include <string.h>
#include "stablo_podatak.h"

Data napravi_pitanje(const char* pitanje)
{
    Data data;
    data.type = QUESTION;
    data.text = strdup(pitanje);
    return data;
}

Data napravi_odgovor(const char* odgovor)
{
    Data data;
    data.type = ANSWER;
    data.text = strdup(odgovor);
    return data;
}

void dodeli(Data *dest, const Data *src)
{
    dest->type = src->type;
    dest->text = strdup(src->text);
}

void ispisi_podatak(const Data* data)
{
    printf("%s", data->text);
}