#include "../include/quiz.h" #include "../include/utils.h" #include #include #include #include #include #include #include #define INPUT_BUFFER_SIZE 64 #define QUIZ_DATA_DIRECTORY "data" #define QUESTIONS_FILE_NAME "questions.txt" #define RESULTS_FILE_NAME "results.csv" #define CSV_TIMESTAMP_BUFFER 32 #define FILE_PATH_BUFFER 256 #define PERMISSIONS_DATA_DIRECTORY 0755 #define TIME_UNLIMITED 0 #define BASE_VALUE 10 static const char* START_PROMPT = "Press ENTER to start..."; static const char* DONE_PROMPT = "Press ENTER when done..."; static const char* TIME_LIMIT_PROMPT = "Time limit (sec, 0 = unlimited): "; static const char* CORRECT_PROMPT = "Correct? (y/n): "; static const char* NEXT_QUESTION_PROMPT = "Next? (y/n): "; static const char* TIME_EXCEEDED_MESSAGE = "Time exceeded!\n"; static const char* CSV_OPEN_ERROR_MESSAGE = "CSV open"; static const char* QUESTIONS_LOAD_ERROR = "Error: cannot load questions file\n"; static const char* DATA_DIR_ERROR = "Error: cannot create data directory\n"; static bool ensure_data_directory_exists(void) { struct stat st = {0}; if (stat(QUIZ_DATA_DIRECTORY, &st) == -1) { #ifdef _WIN32 return _mkdir(QUIZ_DATA_DIRECTORY) == 0; #else return mkdir(QUIZ_DATA_DIRECTORY, PERMISSIONS_DATA_DIRECTORY) == 0; #endif } return true; } static int read_time_limit(void) { char buffer[INPUT_BUFFER_SIZE]; printf("%s", TIME_LIMIT_PROMPT); if (!fgets(buffer, sizeof buffer, stdin)) return TIME_UNLIMITED; errno = 0; char* end = NULL; long value = strtol(buffer, &end, BASE_VALUE); if (errno == ERANGE || end == buffer) return TIME_UNLIMITED; if (value < 0) value = 0; if (value > INT_MAX) value = INT_MAX; return (int)value; } static void write_csv_result(FILE* csv, const char* timestamp, const char* general_q, const char* major_q, const bool correct_answer, const double duration) { static const char* CSV_FORMAT = "\"%s\",\"%s\",\"%s\",%c,%.0f\n"; fprintf(csv, CSV_FORMAT, timestamp, general_q, major_q, correct_answer ? 'y' : 'n', duration); } void print_score(const size_t correct, size_t total) { double percentage = 0.0; if (total != 0) percentage = (double)correct / total * PERCENT_MULTIPLIER; printf(SCORE_FORMAT, correct, total, percentage); } void print_final_score(const QuizSession* session) { double final_percentage = 0.0; if (session->total_answered > 0) final_percentage = (double)session->total_correct / session->total_answered * PERCENT_MULTIPLIER; printf("\nSession score: %.1f%%\n", final_percentage); } // ------------------------ // Initialization & Cleanup // ------------------------ bool initialize_quiz_session(QuizSession* session) { session->seed = (unsigned int)time(NULL); srand(session->seed); if (!ensure_data_directory_exists()) { fprintf(stderr, "%s", DATA_DIR_ERROR); return false; } char questions_path[FILE_PATH_BUFFER]; snprintf(questions_path, sizeof questions_path, "%s/%s", QUIZ_DATA_DIRECTORY, QUESTIONS_FILE_NAME); char results_path[FILE_PATH_BUFFER]; snprintf(results_path, sizeof results_path, "%s/%s", QUIZ_DATA_DIRECTORY, RESULTS_FILE_NAME); if (load_questions(questions_path, &session->questions) != EXIT_SUCCESS) { fprintf(stderr, "%s", QUESTIONS_LOAD_ERROR); return false; } session->csv = fopen(results_path, "a"); if (!session->csv) { perror(CSV_OPEN_ERROR_MESSAGE); free_questions(&session->questions); return false; } session->total_answered = 0; session->total_correct = 0; session->time_limit = read_time_limit(); return true; } void cleanup_session(QuizSession* session) { fclose(session->csv); free_questions(&session->questions); } // ------------------------ // Single Quiz Iteration // ------------------------ bool quiz_iteration(QuizSession* session) { const size_t general_index = get_random_question_index( &session->seed, session->questions.general_count); const size_t major_index = get_random_question_index( &session->seed, session->questions.major_count); const char* general_question = session->questions.general[general_index]; const char* major_question = session->questions.major[major_index]; print_line(); printf( "GENERAL:\n%s\n\nMAJOR:\n%s\n", general_question, major_question); print_line(); wait_for_enter(START_PROMPT); const time_t start_time = time(NULL); wait_for_enter(DONE_PROMPT); const double duration_seconds = difftime(time(NULL), start_time); if (session->time_limit > 0 && duration_seconds > session->time_limit) printf("%s", TIME_EXCEEDED_MESSAGE); const bool correct = ask_yes_no(CORRECT_PROMPT); if (correct) session->total_correct++; session->total_answered++; char timestamp[CSV_TIMESTAMP_BUFFER]; get_answer_timestamp(timestamp, sizeof timestamp); write_csv_result(session->csv, timestamp, general_question, major_question, correct, duration_seconds); print_score(session->total_correct, session->total_answered); return ask_yes_no(NEXT_QUESTION_PROMPT); }