Logo Search packages:      
Sourcecode: kanatest version File versions

test.c

/*
 * Kanatest
 *
 * Copyright (C) 2001-2004, 2006 Tomasz Mąka <pasp@users.sourceforge.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>

#include <glib.h>
#include <gtk/gtk.h>

#include "gui.h"
#include "prefs.h"
#include "main.h"
#include "test.h"
#include "stats.h"
#include "options.h"
#include "i18n.h"


gint question_counter, rwa_counter;
gint wrong_answer_counter, rwa_wrong_answer_counter;
gint max_entries_in_test;

guint test_time;

gint questions_table[MAX_NUMBER_OF_SIGNS];
gint answers_table[MAX_NUMBER_OF_SIGNS];

extern gboolean any_key;
extern gboolean test_state;
extern GtkWidget *main_window;

gchar *kana_signs_names[] = { 
    N_("All kanas"),
    N_("Basic kanas"),
    N_("A-I-U-E-O"),
    N_("KA-KI-KU-KE-KO"),
    N_("SA-SHI-SU-SE-SO"),
    N_("TA-CHI-TSU-TE-TO"),
    N_("NA-NI-NU-NE-NO"),
    N_("HA-HI-FU-HE-HO"),
    N_("MA-MI-MU-ME-MO"),
    N_("YA-YU-YO"),
    N_("RA-RI-RU-RE-RO"),
    N_("WA-WO-N"),
    N_("Combo letters (part 1)"),
    N_("Combo letters (part 2)"),
    N_("User-defined lesson")
};

gchar *kana_mode_names[] = { 
    NULL, N_("Hiragana"), N_("Katakana"), N_("Mixed")
};

gint kana_signs_set_0_idx[NUMBER_OF_SIGNS]; /* All kanas */

gint kana_signs_set_1_idx[] = {      
    0, 1, 2, 3, 4,                  /* A-I-U-E-O */
    5, 6, 7, 8, 9,                  /* KA-KI-KU-KE-KO */
    10, 11, 12, 13, 14,             /* SA-SHI-SU-SE-SO */
    15, 16, 17, 18, 19,             /* TA-CHI-TSU-TE-TO */
    20, 21, 22, 23, 24,             /* NA-NI-NU-NE-NO */
    25, 26, 27, 28, 29,             /* HA-HI-FU-HE-HO */
    30, 31, 32, 33, 34,             /* MA-MI-MU-ME-MO */
    35, 36, 37,                     /* YA-YU-YO */
    38, 39, 40, 41, 42,             /* RA-RI-RU-RE-RO */
    43, 44, 45                      /* WA-WO-N */
};

gint kana_signs_set_2_idx[] = {     /* A-I-U-E-O */
    0, 1, 2, 3, 4
};

gint kana_signs_set_3_idx[] = {     /* KA-KI-KU-KE-KO */
    5, 6, 7, 8, 9
};

gint kana_signs_set_4_idx[] = {     /* SA-SHI-SU-SE-SO */
    10, 11, 12, 13, 14
};

gint kana_signs_set_5_idx[] = {     /* TA-CHI-TSU-TE-TO */
    15, 16, 17, 18, 19
};

gint kana_signs_set_6_idx[] = {     /* NA-NI-NU-NE-NO */
    20, 21, 22, 23, 24
};

gint kana_signs_set_7_idx[] = {     /* HA-HI-FU-HE-HO */
    25, 26, 27, 28, 29
};

gint kana_signs_set_8_idx[] = {     /* MA-MI-MU-ME-MO */
    30, 31, 32, 33, 34
};

gint kana_signs_set_9_idx[] = {     /* YA-YU-YO */
    35, 36, 37
};

gint kana_signs_set_10_idx[] = {    /* RA-RI-RU-RE-RO */
    38, 39, 40, 41, 42
};

gint kana_signs_set_11_idx[] = {    /* WA-WO-N */
    43, 44, 45
};

gint kana_signs_set_12_idx[] = {    /* Combo letters (part 1) */
    46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
    56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
    66, 67, 68, 69, 70
};

gint kana_signs_set_13_idx[] = {    /* Combo letters (part 2) */
    71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
    81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
    91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
    101, 102, 103
};

gint kana_signs_set_14_idx[NUMBER_OF_SIGNS]; /* user-defined-lessons - all kanas reserved */

gint *kana_signs_set_tabs[] = { 
    kana_signs_set_0_idx,
    kana_signs_set_1_idx,
    kana_signs_set_2_idx,
    kana_signs_set_3_idx,
    kana_signs_set_4_idx,
    kana_signs_set_5_idx,
    kana_signs_set_6_idx,
    kana_signs_set_7_idx,
    kana_signs_set_8_idx,
    kana_signs_set_9_idx,
    kana_signs_set_10_idx,
    kana_signs_set_11_idx,
    kana_signs_set_12_idx,
    kana_signs_set_13_idx,
    kana_signs_set_14_idx
};

/* -1 is for number of selected kanas  - offset 14 */
#define USER_DEFINED_LEN_OFFSET 14

gint kana_signs_set_len[] = { 
    104, 46, 5, 5, 5, 5, 5, 5, 5, 3, 5, 3, 25, 33, -1
};

/* romanji | hiragana | katakana */

gchar *kana_signs[] = { 

    /* 0 */             /* 1 */                 /* 2 */                 /* 3 */             /* 4 */
    "a",  "あ", "ア",   "i",   "い", "イ",      "u",   "う", "ウ",      "e",  "え", "エ",   "o",  "お", "オ",
    /* 5 */             /* 6 */                 /* 7 */                 /* 8 */             /* 9 */
    "ka", "か", "カ",   "ki",  "き", "キ",      "ku",  "く", "ク",      "ke", "け", "ケ",   "ko", "こ", "コ",
    /* 10 */            /* 11 */                /* 12 */                /* 13 */            /* 14 */
    "sa", "さ", "サ",   "shi", "し", "シ",      "su",  "す", "ス",      "se", "せ", "セ",   "so", "そ", "ソ",
    /* 15 */            /* 16 */                /* 17 */                /* 18 */            /* 19 */
    "ta", "た", "タ",   "chi", "ち", "チ",      "tsu", "つ", "ツ",      "te", "て", "テ",   "to", "と", "ト",

    /* 20 */            /* 21 */                /* 22 */                /* 23 */            /* 24 */
    "na", "な", "ナ",   "ni", "に", "ニ",       "nu", "ぬ", "ヌ",       "ne", "ね", "ネ",   "no", "の", "ノ",
    /* 25 */            /* 26 */                /* 27 */                /* 28 */            /* 29 */
    "ha", "は", "ハ",   "hi", "ひ", "ヒ",       "fu", "ふ", "フ",       "he", "へ", "ヘ",   "ho", "ほ", "ホ",
    /* 30 */            /* 31 */                /* 32 */                /* 33 */            /* 34 */
    "ma", "ま", "マ",   "mi", "み", "ミ",       "mu", "む", "ム",       "me", "め", "メ",   "mo", "も", "モ",
    /* 35 */                                    /* 36 */                                    /* 37 */
    "ya", "や", "ヤ",                           "yu", "ゆ", "ユ",                           "yo", "よ", "ヨ",
    /* 38 */            /* 39 */                /* 40 */                /* 41 */            /* 42 */
    "ra", "ら", "ラ",   "ri", "り", "リ",       "ru", "る", "ル",       "re", "れ", "レ",   "ro", "ろ", "ロ",

    /* 43 */                                                                                /* 44 */
    "wa", "わ", "ワ",                                                                       "wo", "を", "ヲ",
    /* 45 */
    "n",  "ん", "ン",

    /* 46 */            /* 47 */                /* 48 */                /* 49 */            /* 50 */
    "ga", "が", "ガ",   "gi", "ぎ", "ギ",       "gu", "ぐ", "グ",       "ge", "げ", "ゲ",   "go", "ご", "ゴ",
    /* 51 */            /* 52 */                /* 53 */                /* 54 */            /* 55 */
    "za", "ざ", "ザ",   "ji", "じ", "ジ",       "zu", "ず", "ズ",       "ze", "ぜ", "ゼ",   "zo", "ぞ", "ゾ",
    /* 56 */            /* 57 */                /* 58 */                /* 59 */            /* 60 */
    "da", "だ", "ダ",   "di", "ぢ", "ヂ",       "du", "づ", "ヅ",       "de", "で", "デ",   "do", "ど", "ド",
    /* 61 */            /* 62 */                /* 63 */                /* 64 */            /* 65 */
    "ba", "ば", "バ",   "bi", "び", "ビ",       "bu", "ぶ", "ブ",       "be", "べ", "ベ",   "bo", "ぼ", "ボ",
    /* 66 */            /* 67 */                /* 68 */                /* 69 */            /* 70 */
    "pa", "ぱ", "パ",   "pi", "ぴ", "ピ",       "pu", "ぷ", "プ",       "pe", "ぺ", "ペ",   "po", "ぽ", "ポ",

    /* 71 */                                    /* 72 */                                    /* 73 */
    "kya", "きゃ", "キャ",                      "kyu", "きゅ", "キュ",                      "kyo", "きょ", "キョ",
    /* 74 */                                    /* 75 */                                    /* 76 */
    "sha", "しゃ", "シャ",                      "shu", "しゅ", "シュ",                      "sho", "しょ", "ショ",
    /* 77 */                                    /* 78 */                                    /* 79 */
    "cha", "ちゃ", "チャ",                      "chu", "ちゅ", "チュ",                      "cho", "ちょ", "チョ",
    /* 80 */                                    /* 81 */                                    /* 82 */
    "nya", "にゃ", "ニャ",                      "nyu", "にゅ", "ニュ",                      "nyo", "にょ", "ニョ",
    /* 83 */                                    /* 84 */                                    /* 85 */
    "hya", "ひゃ", "ヒャ",                      "hyu", "ひゅ", "ヒュ",                      "hyo", "ひょ", "ヒョ",
    /* 86 */                                    /* 87 */                                    /* 88 */
    "mya", "みゃ", "ミャ",                      "myu", "みゅ", "ミュ",                      "myo", "みょ", "ミョ",
    /* 89 */                                    /* 90 */                                    /* 91 */
    "rya", "りゃ", "リャ",                      "ryu", "りゅ", "リュ",                      "ryo", "りょ", "リョ",

    /* 92 */                                    /* 93 */                                    /* 94 */
    "gya", "ぎゃ", "ギャ",                      "gyu",  "ぎゅ", "ギュ",                     "gyo", "ぎょ", "ギョ", 
    /* 95 */                                    /* 96 */                                    /* 97 */
    "ja",  "じゃ", "ジャ",                      "ju",   "じゅ", "ジュ",                     "jo",  "じょ", "ジョ", 
    /* 98 */                                    /* 99 */                                    /* 100 */
    "bya", "びゃ", "ビャ",                      "byu",  "びゅ", "ビュ",                     "byo", "びょ", "ビョ", 
    /* 101 */                                   /* 102 */                                   /* 103 */
    "pya", "ぴゃ", "ピャ",                      "pyu",  "ぴゅ", "ピュ",                     "pyo", "ぴょ", "ピョ"

};


/*--------------------------------------------------------------------*/

void 
test_init (void) {

    gint i, pos;

    pos = 0;

    for (i=0; i < NUMBER_OF_SIGNS; i++) {
        kana_signs_set_0_idx[i] = i;

        if (config.user_defined_lesson[i] == '+') {
            kana_signs_set_14_idx[pos++] = i;
        }
    }

    kana_signs_set_len[USER_DEFINED_LEN_OFFSET] = pos;   /* replace -1 with number of selected kanas */

    max_entries_in_test = kana_signs_set_len[config.kana_set] * ((config.kana_mode == MIXED) ? 2 : 1);

    question_counter = 0;
    wrong_answer_counter = 0;
    rwa_wrong_answer_counter = 0;
    rwa_counter = 0;
}

/*--------------------------------------------------------------------*/

void 
test_generate_tables (gint number_of_questions) {

    gint i, tmp;
    gint shuffles, source_pos, dest_pos;
    struct timeval timer;
    gint *table;

    /* set seed */
    gettimeofday(&timer, NULL);
    srand48(timer.tv_usec + getpid());

    table = kana_signs_set_tabs[config.kana_set];

    /* fill questions and answers tables */
    for (i = 0; i < number_of_questions; i++) {

        if (i >= kana_signs_set_len[config.kana_set]) {
            questions_table[i] = table[i-kana_signs_set_len[config.kana_set]] + MIXED_SEPARATOR;
        } else {
            questions_table[i] = table[i];
        }

        answers_table[i] = 0;
    }

    shuffles = (gint)(number_of_questions * 32.0 * drand48());    /* max 32 shuffles */

    for (i = 0; i < shuffles; i++) {

        /* generate two unique numbers */
        do {
            source_pos = (gint)(number_of_questions * drand48());
            dest_pos = (gint)(number_of_questions * drand48());
        } while (source_pos == dest_pos);

        /* exchange numbers src<->dst */
        tmp = questions_table[source_pos];
        questions_table[source_pos] = questions_table[dest_pos];
        questions_table[dest_pos] = tmp;
    }
}

/*--------------------------------------------------------------------*/

gchar* 
test_sec2str (guint seconds, gboolean mshort) {

    static gchar obuffer[BUFFER_SIZE];
    guint minutes;

    if (seconds > 60) {
        minutes = seconds / 60;
        seconds -= minutes * 60;

        if (minutes == 1) {

            if (mshort == TRUE) {
                sprintf (obuffer, "01:%02d", seconds);
            } else {
                sprintf (obuffer, "1 %s %s %d %s", _("minute"), _("and"), seconds, _("seconds"));
            }

        } else {

            if (seconds == 1) {
                if (mshort == TRUE) {
                    sprintf (obuffer, "%02d:01", minutes);
                } else {
                    sprintf (obuffer, "%d %s %s 1 %s", minutes, _("minutes"), _("and"), _("second"));
                }
            } else {
                if (mshort == TRUE) {
                    sprintf (obuffer, "%02d:%02d", minutes, seconds);
                } else {
                    sprintf (obuffer, "%d %s %s %d %s", minutes, _("minutes"), _("and"), seconds, _("seconds"));
                }
            }
        }

    } else {

        if (seconds == 1) {
            if (mshort == TRUE) {
                sprintf (obuffer, "00:01");
            } else {
                sprintf (obuffer, "1 %s", _("second"));
            }
        } else {
            if (mshort == TRUE) {
                sprintf (obuffer, "00:%02d", seconds);
            } else {
                sprintf (obuffer, "%d %s", seconds, _("second"));
            }
        }
    }

    return obuffer;
}

/*--------------------------------------------------------------------*/

void 
test_check_answer (gchar *user_answer) {

    gchar answer[ANSWER_LEN], tmp_a[BUFFER_SIZE], tmp_b[BUFFER_SIZE];
    gint kana_number, wrongly_answered_questions;
    gint i;
    gboolean correct_answer;

    kana_number = questions_table[question_counter];

    if(kana_number >= MIXED_SEPARATOR) {    /* mixed mode ? */
        kana_number -= MIXED_SEPARATOR;
    }

    strncpy (answer, kana_signs[kana_number*3], ANSWER_LEN);
 
    correct_answer = FALSE;

    if (!strcmp(user_answer, answer)) {
        correct_answer = TRUE;
    }

    if(!strcmp(answer, "di") && (!strcmp(user_answer, "di") || !strcmp(user_answer, "ji"))) {
        correct_answer = TRUE;
    }
    if(!strcmp(answer, "du") && (!strcmp(user_answer, "du") || !strcmp(user_answer, "zu"))) {
        correct_answer = TRUE;
    }

    if (correct_answer == FALSE) {

        /* WRONG ANSWER */

        if (config.ca_timeout != TO_DISABLED) {  

            any_key = FALSE;

            gtk_widget_set_sensitive (romaji_entry, FALSE);
            gtk_widget_set_sensitive (stop_button, FALSE);

            gui_display_kana (questions_table[question_counter], ROMAJI);
            while (g_main_context_iteration(NULL, FALSE));

            if (config.ca_timeout != TO_ANYKEY) {

                g_usleep (config.ca_timeout * 1000000);

            } else {    /* waiting for any key */

                strcpy (tmp_a, gtk_entry_get_text (GTK_ENTRY(romaji_entry)));
                sprintf(tmp_b, "%s (Press any key)", tmp_a);
                gtk_entry_set_max_length(GTK_ENTRY(romaji_entry), 32);

                while (any_key != TRUE) {
                    gtk_entry_set_text (GTK_ENTRY(romaji_entry), tmp_b);
                    while (g_main_context_iteration(NULL, FALSE));
                    g_usleep (250000);
                    if (any_key) {
                        break;
                    }
                    gtk_entry_set_text (GTK_ENTRY(romaji_entry), tmp_a);
                    while (g_main_context_iteration(NULL, FALSE));
                    g_usleep (200000);
                }
                
                gtk_entry_set_max_length(GTK_ENTRY(romaji_entry), 3);
                gtk_entry_set_text (GTK_ENTRY(romaji_entry), tmp_a);
                while (g_main_context_iteration(NULL, FALSE));
            }

            gtk_widget_set_sensitive (romaji_entry, TRUE);
            gtk_widget_set_sensitive (stop_button, TRUE);

            gtk_widget_grab_focus (romaji_entry);

        }

        wrong_answer_counter++;
        answers_table[question_counter] = 1;

    } else {

        /* CORRECT ANSWER */

        if (config.repeat_wrong == TRUE) {
            questions_table[question_counter] = -1;
            answers_table[question_counter] = 0;
        }

    }

    if (config.repeat_wrong == TRUE) {

        i = 0;  /* index to first question in the table */

        do {
            question_counter = (question_counter + 1) % max_entries_in_test;    /* next question */

            if (i == max_entries_in_test) { /* all questions ? */
                break;
            }

            i++;    /* next pos */

        } while ((questions_table[question_counter]) == -1);    /* -1 is when */

        rwa_counter++;

        if (rwa_counter == max_entries_in_test) {
            rwa_wrong_answer_counter = wrong_answer_counter;
        }

    } else {
        question_counter = (question_counter + 1) % max_entries_in_test;    /* next question */
    }

    gui_set_progress();

    if (config.repeat_wrong == FALSE) {

        /* RWA is disabled */

        if (question_counter == 0) {

            test_info();    /* finish! */

        } else {

            gui_display_kana (questions_table[question_counter], config.kana_mode);
        }

    } else {

        /* RWA is enabled */

        /* count wrongly answered questions */
        for (i = wrongly_answered_questions = 0; i < max_entries_in_test; i++) {
            wrongly_answered_questions += answers_table[i];
        }

        if (wrongly_answered_questions == 0 && rwa_counter >= max_entries_in_test) {

                wrong_answer_counter = rwa_wrong_answer_counter;
                test_info();    /* finish! */

        } else if (questions_table[question_counter] != -1) {

                gui_display_kana (questions_table[question_counter], config.kana_mode);
        }
    }
}

/*--------------------------------------------------------------------*/

void 
test_info(void) {

    GtkWidget *info_dialog;
    gchar   message[BUFFER_SIZE];
    struct tm   *timer;
    time_t      tmm;

    test_time = difftime (time(NULL), start_time);

    test_state = FALSE;
    gui_disable_test ();

    sprintf(message, "<span font_desc='16'><b>%s</b></span>\n\n"
            "<span font_desc='12'>%s %d\t\t\t\n"
            "%s %d\n%s %d\n"
            "\n%s %.f %%\n"
            "\n%s %s\n</span>",
            _("Test has finished!"), _("Total questions:"),
            max_entries_in_test, 
            _("Correct answers:"),
            max_entries_in_test-wrong_answer_counter, 
            _("Wrong answers:"),
            wrong_answer_counter,
            _("Correctness ratio:"),
            (gfloat)(max_entries_in_test-wrong_answer_counter)/max_entries_in_test*100.0, 
            _("Drilling-time:"),
            test_sec2str(test_time, FALSE));

    info_dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW(main_window), 
                                          GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
                                          GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, message);

    gtk_window_set_title(GTK_WINDOW(info_dialog), _("Information"));
    gtk_widget_show (info_dialog);
    gtk_dialog_run(GTK_DIALOG(info_dialog));
    gtk_widget_destroy(info_dialog);

    tmm = time(NULL); 
    timer = localtime(&tmm);

    stats_add_entry (timer->tm_mday, timer->tm_mon, timer->tm_year+1900, timer->tm_hour, timer->tm_min,
                     (guint) test_time, config.kana_mode, config.kana_set, max_entries_in_test, 
                     max_entries_in_test-wrong_answer_counter, config.repeat_wrong);

}

/*--------------------------------------------------------------------*/



Generated by  Doxygen 1.6.0   Back to index