[Tux4kids-commits] r1293 - in tuxmath/branches/lan: server src
David Bruce
dbruce-guest at alioth.debian.org
Thu Jul 30 02:27:03 UTC 2009
Author: dbruce-guest
Date: 2009-07-30 02:27:03 +0000 (Thu, 30 Jul 2009)
New Revision: 1293
Added:
tuxmath/branches/lan/src/mathcards.c
tuxmath/branches/lan/src/mathcards.h
Removed:
tuxmath/branches/lan/server/mathcards.c
tuxmath/branches/lan/server/mathcards.h
tuxmath/branches/lan/server/transtruct.h
tuxmath/branches/lan/src/mathcards.c
tuxmath/branches/lan/src/mathcards.h
Modified:
tuxmath/branches/lan/server/Makefile.am
tuxmath/branches/lan/server/server.c
tuxmath/branches/lan/server/testclient.c
tuxmath/branches/lan/src/network.c
tuxmath/branches/lan/src/network.h
tuxmath/branches/lan/src/transtruct.h
Log:
now only using copy of mathcards.h/c from src, same with transtruct.h
SendQuestion() no longer used, now always use add_question.
Modified: tuxmath/branches/lan/server/Makefile.am
===================================================================
--- tuxmath/branches/lan/server/Makefile.am 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/server/Makefile.am 2009-07-30 02:27:03 UTC (rev 1293)
@@ -3,10 +3,13 @@
bin_PROGRAMS = tuxmathserver tuxmathtestclient
-tuxmathserver_SOURCES = server.c mathcards.c ../src/throttle.c
+tuxmathserver_SOURCES = server.c \
+ ../src/mathcards.c \
+ ../src/throttle.c
+
tuxmathtestclient_SOURCES = testclient.c \
../src/throttle.c \
../src/network.c
-EXTRA_DIST = mathcards.h server.h testclient.h
+EXTRA_DIST = server.h testclient.h
Deleted: tuxmath/branches/lan/server/mathcards.c
===================================================================
--- tuxmath/branches/lan/server/mathcards.c 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/server/mathcards.c 2009-07-30 02:27:03 UTC (rev 1293)
@@ -1,2614 +0,0 @@
-/*
-* C Implementation: mathcards.c
-*
-* Description: implementation of backend for a flashcard-type math game.
- Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
- (aka tuxmath). (If tuxmath were a C++ program, this would be a C++ class).
- MathCards could be used as the basis for similar games using a different interface.
-
-*
-*
-* Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2005
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*
-* Revised extensively in 2008 by Brendan Luchen, Tim Holy, and David Bruce
-*
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-
-
-#include "transtruct.h"
-#include "mathcards.h"
-
-/* extern'd constants */
-
-const char* const MC_OPTION_TEXT[NOPTS+1] = {
-"PLAY_THROUGH_LIST",
-"QUESTION_COPIES",
-"REPEAT_WRONGS",
-"COPIES_REPEATED_WRONGS",
-"ALLOW_NEGATIVES",
-"MAX_ANSWER",
-"MAX_QUESTIONS",
-"MAX_FORMULA_NUMS",
-"MIN_FORMULA_NUMS",
-
-"FORMAT_ANSWER_LAST",
-"FORMAT_ANSWER_FIRST",
-"FORMAT_ANSWER_MIDDLE",
-"FORMAT_ADD_ANSWER_LAST",
-"FORMAT_ADD_ANSWER_FIRST",
-"FORMAT_ADD_ANSWER_MIDDLE",
-"FORMAT_SUB_ANSWER_LAST",
-"FORMAT_SUB_ANSWER_FIRST",
-"FORMAT_SUB_ANSWER_MIDDLE",
-"FORMAT_MULT_ANSWER_LAST",
-"FORMAT_MULT_ANSWER_FIRST",
-"FORMAT_MULT_ANSWER_MIDDLE",
-"FORMAT_DIV_ANSWER_LAST",
-"FORMAT_DIV_ANSWER_FIRST",
-"FORMAT_DIV_ANSWER_MIDDLE",
-
-"ADDITION_ALLOWED",
-"SUBTRACTION_ALLOWED",
-"MULTIPLICATION_ALLOWED",
-"DIVISION_ALLOWED",
-"TYPING_PRACTICE_ALLOWED",
-"ARITHMETIC_ALLOWED",
-"COMPARISON_ALLOWED",
-
-"MIN_AUGEND",
-"MAX_AUGEND",
-"MIN_ADDEND",
-"MAX_ADDEND",
-
-"MIN_MINUEND",
-"MAX_MINUEND",
-"MIN_SUBTRAHEND",
-"MAX_SUBTRAHEND",
-
-"MIN_MULTIPLIER",
-"MAX_MULTIPLIER",
-"MIN_MULTIPLICAND",
-"MAX_MULTIPLICAND",
-
-"MIN_DIVISOR",
-"MAX_DIVISOR",
-"MIN_QUOTIENT",
-"MAX_QUOTIENT",
-
-"MIN_TYPING_NUM",
-"MAX_TYPING_NUM",
-
-"MIN_COMPARATOR" ,
-"MAX_COMPARATOR" ,
-"MIN_COMPARISAND",
-"MAX_COMPARISAND",
-
-"RANDOMIZE",
-
-"COMPREHENSIVE",
-"AVG_LIST_LENGTH",
-"VARY_LIST_LENGTH",
-
-"END_OF_OPTS"
-};
-
-
-
-const int MC_DEFAULTS[] = {
- 1, //PLAY_THROUGH_LIST
- 1, //QUESTION_COPIES
- 1, //REPEAT_WRONGS
- 1, //COPIES_REPEATED_WRONGS
- 0, //ALLOW_NEGATIVES
- 999, //MAX_ANSWER
- 5000, //MAX_QUESTIONS
- 2, //MAX_FORMULA_NUMS
- 2, //MIN_FORMULA_NUMS
- //
- 1, //FORMAT_ANSWER_LAST
- 0, //FORMAT_ANSWER_FIRST
- 0, //FORMAT_ANSWER_MIDDLE
- 1, //FORMAT_ADD_ANSWER_LAST
- 0, //FORMAT_ADD_ANSWER_FIRST
- 0, //FORMAT_ADD_ANSWER_MIDDLE
- 1, //FORMAT_SUB_ANSWER_LAST
- 0, //FORMAT_SUB_ANSWER_FIRST
- 0, //FORMAT_SUB_ANSWER_MIDDLE
- 1, //FORMAT_MULT_ANSWER_LAST
- 0, //FORMAT_MULT_ANSWER_FIRST
- 0, //FORMAT_MULT_ANSWER_MIDDLE
- 1, //FORMAT_DIV_ANSWER_LAST
- 0, //FORMAT_DIV_ANSWER_FIRST
- 0, //FORMAT_DIV_ANSWER_MIDDLE
- //
- 1, //ADDITION_ALLOWED
- 1, //SUBTRACTION_ALLOWED
- 1, //MULTIPLICATION_ALLOWED
- 1, //DIVISION_ALLOWED
-
- 0, //TYPING_PRACTICE_ALLOWED
- 1, //ARITHMETIC_ALLOWED
- 0, //COMPARISON_ALLOWED
- //
- 0, //MIN_AUGEND
- 12, //MAX_AUGEND
- 0, //MIN_ADDEND
- 12, //MAX_ADDEND
- //
- 0, //MIN_MINUEND
- 12, //MAX_MINUEND
- 0, //MIN_SUBTRAHEND
- 12, //MAX_SUBTRAHEND
- //
- 0, //MIN_MULTIPLIER
- 12, //MAX_MULTIPLIER
- 0, //MIN_MULTIPLICAND
- 12, //MAX_MULTIPLICAND
- //
- 0, //MIN_DIVISOR
- 12, //MAX_DIVISOR
- 0, //MIN_QUOTIENT
- 12, //MAX_QUOTIENT
- //
- 0, //MIN_TYPING_NUM
- 12, //MAX_TYPING_NUM
- //
- 0, //MIN_COMPARATOR
- 12, //MAX_COMPARATOR
- 0, //MIN_COMPARISAND
- 12, //MAX_COMPARISAND
-
- 1, //RANDOMIZE
-
- 0, //COMPREHENSIVE
- 100, //AVG_LIST_LENGTH
- 1 //VARY_LIST_LENGTH
-};
-
-
-
-/* "Globals" for mathcards.c: */
-#define PI_VAL 3.1415927
-#define NPRIMES 9
-const int smallprimes[NPRIMES] = {2, 3, 5 ,7, 11, 13, 17, 19, 23};
-const char operchars[4] = "+-*/";
-extern int n;
-
-MC_Options* math_opts = 0;
-MC_MathQuestion* question_list = 0;
-MC_MathQuestion* wrong_quests = 0;
-MC_MathQuestion* active_quests = 0;
-MC_MathQuestion* next_wrong_quest = 0;
-int initialized = 0;
-int quest_list_length = 0;
-int answered_correctly = 0;
-int answered_wrong = 0;
-int questions_pending = 0;
-int unanswered = 0;
-int starting_length = 0;
-//NOTE these are no longer used:
-int max_formula_size = 0; //max length in chars of a flashcard's formula
-int max_answer_size = 0; //and of its answer
-
-/* For keeping track of timing data */
-/*FIXME do we really need any of these? */
-float* time_per_question_list = NULL;
-int length_time_per_question_list = 0;
-int length_alloc_time_per_question_list = 0;
-
-const MC_FlashCard DEFAULT_CARD = {0,0,0,0}; //empty card to signal error
-
-/* "private" function prototypes: */
-/* */
-/* these are for internal use by MathCards only - like */
-/* the private functions of a C++ class. Declared static */
-/* to give file scope rather than extern scope. */
-
-static MC_MathQuestion* generate_list(void);
-static void clear_negatives(void);
-//static int validate_question(int n1, int n2, int n3);
-//static MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f);
-static MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard);
-static MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node);
-static MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node);
-static MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n);
-static MC_MathQuestion* delete_list(MC_MathQuestion* list);
-//static int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy);
-static int list_length(MC_MathQuestion* list);
-static int randomize_list(MC_MathQuestion** list);
-
-int comp_randomizer(const void *a, const void *b);
-static MC_MathQuestion* pick_random(int length, MC_MathQuestion* list);
-static int compare_node(MC_MathQuestion* first, MC_MathQuestion* other);
-static int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr);
-//static int int_to_bool(int i);
-//static int sane_value(int i);
-//static int abs_value(int i);
-static int log10i(int i);
-static int floatCompare(const void *v1,const void *v2);
-
-static void print_list(FILE* fp,MC_MathQuestion* list);
-void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length);
-
-/* these functions are dead code unless compiling with debug turned on: */
-#ifdef MC_DEBUG
-static void print_card(MC_FlashCard card);
-static void print_counters(void);
-//static MC_MathQuestion* create_node_copy(MC_MathQuestion* other);
-//static MC_FlashCard create_card_from_node(MC_MathQuestion* node);
-#endif
-
-/* Functions for new mathcards architecture */
-static void free_node(MC_MathQuestion* mq); //wrapper for free() that also frees card
-static MC_FlashCard generate_random_flashcard(void);
-static MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat);
-static void copy_card(const MC_FlashCard* src, MC_FlashCard* dest); //deep copy a flashcard
-static MC_MathQuestion* allocate_node(void); //allocate space for a node
-static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b); //test for identical cards
-static int find_divisor(int a); //return a random positive divisor of a
-static int calc_num_valid_questions(void);
-static MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list);
-static MC_MathQuestion* find_node(MC_MathQuestion* list, int num);
-
-/* MC_Initialize() sets up the struct containing all of */
-/* settings regarding math questions. It should be */
-/* called before any other function. Many of the other */
-/* functions will not work properly if MC_Initialize() */
-/* has not been called. It only needs to be called once, */
-/* i.e when the program is starting, not at the beginning*/
-/* of each math game for the player. Returns 1 if */
-/* successful, 0 otherwise. */
-int MC_Initialize(void)
-{
- int i;
-
- mcdprintf("\nEntering MC_Initialize()");
- /* check flag to see if we did this already */
- if (initialized)
- {
-
- #ifdef MC_DEBUG
- printf("\nAlready initialized");
- MC_PrintMathOptions(stdout, 0);
- printf("\nLeaving MC_Initialize()\n");
- #endif
-
- return 1;
- }
- math_opts = malloc(sizeof(MC_Options));
- /* bail out if no struct */
- if (!math_opts)
- {
- mcdprintf("\nError: malloc couldn't allocate math_opts for some reason\n");
- mcdprintf("\nLeaving MC_Initialize()\n");
-
- fprintf(stderr, "\nUnable to initialize math_options");
- return 0;
- }
-
- /* set defaults */
- for (i = 0; i < NOPTS; ++i)
- {
- math_opts->iopts[i] = MC_DEFAULTS[i];
- }
-
- /* if no negatives to be used, reset any negatives to 0 */
- if (!math_opts->iopts[ALLOW_NEGATIVES])
- {
- clear_negatives();
- }
-
- initialized = 1;
-
- #ifdef MC_DEBUG
- MC_PrintMathOptions(stdout, 0);
- printf("\nLeaving MC_Initialize()\n");
- #endif
-
- return 1;
-}
-
-
-
-/* MC_StartGame() generates the list of math questions */
-/* based on existing settings. It should be called at */
-/* the beginning of each math game for the player. */
-/* Returns 1 if resultant list contains 1 or more */
-/* questions, 0 if list empty or not generated */
-/* successfully. */
-int MC_StartGame(void)
-{
-
- mcdprintf("\nEntering MC_StartGame()");
-
- /* if math_opts not set up yet, initialize it: */
- if (!initialized)
- {
-
- mcdprintf("\nNot initialized - calling MC_Initialize()");
-
- MC_Initialize();
- }
-
- if (!math_opts)
- {
- mcdprintf("\nCould not initialize - bailing out");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 0;
- }
- /* we know math_opts exists if we make it to here */
- srand(time(NULL));
-
- /* clear out old lists if starting another game: (if not done already) */
- delete_list(question_list);
- question_list = NULL;
- delete_list(wrong_quests);
- wrong_quests = NULL;
- delete_list(active_quests);
- active_quests = NULL;
-
- /* clear the time list */
- if (time_per_question_list != NULL) {
- free(time_per_question_list);
- time_per_question_list = NULL;
- length_time_per_question_list = 0;
- length_alloc_time_per_question_list = 0;
- }
-
- //NOTE this is going away - complicates code too much to calculate this to
- //save a few bytes rather than making the string size constant
- /* determine how much space needed for strings, based on user options */
- max_formula_size = MC_GetOpt(MAX_FORMULA_NUMS)
- * (log10i(MC_GLOBAL_MAX) + 4) //sign/operator/spaces
- + 1; //question mark for answer
- max_answer_size = (int)(log10i(MC_GLOBAL_MAX) ) + 2; //negative sign + digit
-
- mcdprintf("max answer, formula size: %d, %d\n",
- max_answer_size, max_formula_size);
-
- question_list = generate_list();
-
- next_wrong_quest = 0;
- /* initialize counters for new game: */
- quest_list_length = list_length(question_list);
-
-
- /* Note: the distinction between quest_list_length and */
- /* unanswered is that the latter includes questions */
- /* that are currently "in play" by the user interface - */
- /* it is only decremented when an answer to the question*/
- /* is received. */
- unanswered = starting_length = quest_list_length;
- answered_correctly = 0;
- answered_wrong = 0;
- questions_pending = 0;
-
- #ifdef MC_DEBUG
- print_counters();
- #endif
-
- /* make sure list now exists and has non-zero length: */
- if (question_list && quest_list_length)
- {
- mcdprintf("\nGame set up successfully");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 1;
- }
- else
- {
- mcdprintf("\nGame NOT set up successfully - no valid list");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 0;
- }
-}
-
-/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
-/* but uses the incorrectly answered questions from the */
-/* previous game for the question list as a review form */
-/* of learning. If there were no wrong answers (or no */
-/* previous game), it behaves just like MC_StartGame(). */
-/* FIXME wonder if it should return a different value if */
-/* the list is created from settings because there is no */
-/* valid wrong question list? */
-int MC_StartGameUsingWrongs(void)
-{
- mcdprintf("\nEntering MC_StartGameUsingWrongs()");
-
- /* Note: if not initialized, control will pass to */
- /* MC_StartGame() via else clause so don't need to test */
- /* for initialization here */
- if (wrong_quests &&
- list_length(wrong_quests))
- {
- mcdprintf("\nNon-zero length wrong_quests list found, will");
- mcdprintf("\nuse for new game list:");
-
- /* initialize lists for new game: */
- delete_list(question_list);
- if(!randomize_list(&wrong_quests))
- {
- fprintf(stderr, "Error during randomization of wrong_quests!\n");
- /* Punt on trying wrong question list, just run normal game */
- return MC_StartGame();
- }
- question_list = wrong_quests;
- wrong_quests = 0;
- next_wrong_quest = 0;
- delete_list(active_quests);
- active_quests = 0;
- /* initialize counters for new game: */
- quest_list_length = list_length(question_list);
- unanswered = starting_length = quest_list_length;
- answered_correctly = 0;
- answered_wrong = 0;
- questions_pending = 0;
-
- #ifdef MC_DEBUG
- print_counters();
- print_list(stdout, question_list);
- printf("\nLeaving MC_StartGameUsingWrongs()\n");
- #endif
-
- return 1;
- }
- else /* if no wrong_quests list, go to MC_StartGame() */
- /* to set up list based on math_opts */
- {
- mcdprintf("\nNo wrong questions to review - generate list from math_opts\n");
- mcdprintf("\nLeaving MC_StartGameUsingWrongs()\n");
-
- return MC_StartGame();
- }
-}
-
-
-/* MC_NextQuestion() takes a pointer to an allocated */
-/* MC_MathQuestion struct and fills in the fields for */
-/* use by the user interface program. It basically is */
-/* like taking the next flashcard from the pile. The */
-/* node containing the question is removed from the list. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-/* or if argument pointer is invalid. */
-int MC_NextQuestion(MC_FlashCard* fc)
-{
- mcdprintf("\nEntering MC_NextQuestion()\n");
-
- /* (so we can move the node into active_quests:) */
- MC_MathQuestion* ptr;
-
- if (!fc )
- {
- fprintf(stderr, "\nNull MC_FlashCard* argument!\n");
- mcdprintf("\nLeaving MC_NextQuestion()\n");
- return 0;
- }
-
- if (!question_list ||
-/* !next_question || */
- !list_length(question_list) )
- {
- mcdprintf("\nquestion_list invalid or empty");
- mcdprintf("\nLeaving MC_NextQuestion()\n");
-
- return 0;
- }
-
- /* 'draw' - copy over the first question */
- copy_card(&question_list->card, fc);
-
- /* take first question node out of list and move it into active_quests list: */
- ptr = question_list;
- question_list = remove_node(question_list, ptr);
-// free_node(ptr);
- quest_list_length--;
- questions_pending++;
- active_quests = append_node(active_quests, ptr);
-
- #ifdef MC_DEBUG
- printf("\nnext question is:");
- print_card(*fc);
- print_counters();
- printf("\n\nLeaving MC_NextQuestion()\n");
- #endif
-
- return 1;
-}
-
-
-
-/* MC_AnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has been answered */
-/* correctly. Returns 1 if no errors. */
-int MC_AnsweredCorrectly(MC_FlashCard* fc)
-{
- mcdprintf("\nEntering MC_AnsweredCorrectly()");
-
- MC_MathQuestion* quest = NULL;
-
- if (!fc)
- {
- fprintf(stderr, "\nMC_AnsweredCorrectly() passed invalid pointer as argument!\n");
-
- mcdprintf("\nInvalid MC_FlashCard* argument!");
- mcdprintf("\nLeaving MC_AnsweredCorrectly()\n");
-
- return 0;
- }
-
- if(!active_quests) // No questions currently "in play" - something is wrong:
- {
- fprintf(stderr, "MC_AnsweredCorrectly() - active_quests empty\n");
- return 0;
- }
-
- #ifdef MC_DEBUG
- printf("\nQuestion was:");
- print_card(*fc);
- #endif
-
-
- //First take the question out of the active_quests list
- quest = active_quests;
- // Loop until quest is NULL or we find card with same id:
- while(quest && (fc->question_id != quest->card.question_id))
- quest = quest->next;
- if(!quest) // Means we didn't find matching card - something is wrong:
- {
- fprintf(stderr, "MC_AnsweredCorrectly() - matching question not found!\n");
- return 0;
- }
- #ifdef MC_DEBUG
- printf("\nMatching question is:");
- print_card(quest->card);
- #endif
-
- //We found a matching question, now we take it out of the
- //"active_quests" list and either put it back into the
- //main question list in a random location, or delete it:
- active_quests = remove_node(active_quests, quest);
- questions_pending--; //the length of the 'active_quests' list
-
-
- answered_correctly++;
-
- if (!math_opts->iopts[PLAY_THROUGH_LIST])
- /* reinsert question into question list at random location */
- {
- mcdprintf("\nReinserting question into list");
-
- MC_MathQuestion* rand_spot;
- /* put it into list */
- rand_spot = pick_random(quest_list_length, question_list);
- question_list = insert_node(question_list, rand_spot, quest);
- quest_list_length++;
- /* unanswered does not change - was not decremented when */
- /* question allocated! */
- }
- else
- {
- mcdprintf("\nNot reinserting question into list");
- free_node(quest);
- /* not recycling questions so fewer questions remain: */
- unanswered--;
- }
-
- #ifdef MC_DEBUG
- print_counters();
- printf("\nLeaving MC_AnsweredCorrectly()\n");
- #endif
-
- return 1;
-}
-
-
-
-int MC_AnsweredCorrectly_id(int id)
-{
- MC_MathQuestion* mq;
- MC_FlashCard* fc;
-
- if(!active_quests)
- {
- mcdprintf("MC_AnsweredCorrectly_id() - active_quests is empty\n");
- return 0;
- }
- //Find the question with the given id, if it exists:
- //First take the question out of the active_quests list
- mq = active_quests;
- // Loop until mq is NULL or card found with matching id:
- while(mq && (id != mq->card.question_id))
- {
- mcdprintf("id is %d, mq->card.question_id is %d\n", id, mq->card.question_id);
- mq = mq->next;
- }
- if(!mq) // Means we didn't find matching card - something is wrong:
- {
- fprintf(stderr, "MC_AnsweredCorrectly_id() - matching question not found for id = %d!\n", id);
- return 0;
- }
- //Now just pass address of card field to MC_AnsweredCorrectly():
- fc = &(mq->card);
- return MC_AnsweredCorrectly(fc);
-}
-
-
-
-/* MC_NotAnsweredCorrectly() is how the user interface */
-/* tells MathCards that the player failed to answer the */
-/* question correctly. Returns 1 if no errors. */
-/* Note: this gets triggered only if a player's igloo/city */
-/* gets hit by a question, not if they "miss". */
-int MC_NotAnsweredCorrectly(MC_FlashCard* fc)
-{
- mcdprintf("\nEntering MC_NotAnsweredCorrectly()");
-
- MC_MathQuestion* quest = NULL;
-
- if (!fc)
- {
- fprintf(stderr, "\nMC_NotAnsweredCorrectly() passed invalid pointer as argument!\n");
-
- mcdprintf("\nInvalid MC_FlashCard* argument!");
- mcdprintf("\nLeaving MC_NotAnsweredCorrectly()\n");
-
- return 0;
- }
-
- if(!active_quests) // No questions currently "in play" - something is wrong:
- {
- fprintf(stderr, "MC_NotAnsweredCorrectly() - active_quests empty\n");
- return 0;
- }
-
- #ifdef MC_DEBUG
- printf("\nQuestion was:");
- print_card(*fc);
- #endif
-
-
- //First take the question out of the active_quests list
- quest = active_quests;
- // Loop until quest is NULL or we find card with same id:
- while(quest && (fc->question_id != quest->card.question_id))
- quest = quest->next;
- if(!quest) // Means we didn't find matching card - something is wrong:
- {
- fprintf(stderr, "MC_NotAnsweredCorrectly() - matching question not found!\n");
- return 0;
- }
- #ifdef MC_DEBUG
- printf("\nMatching question is:");
- print_card(quest->card);
- #endif
-
- //We found a matching question, now we take it out of the
- //"active_quests" list and either put it back into the
- //main question list in a random location, or delete it:
- active_quests = remove_node(active_quests, quest);
- questions_pending--; //the length of the 'active_quests' list
-
- answered_wrong++;
-
- /* add question to wrong_quests list: */
- if (!already_in_list(wrong_quests, quest)) /* avoid duplicates */
- {
- mcdprintf("\nAdding to wrong_quests list");
- wrong_quests = append_node(wrong_quests, quest);
- }
- else /* avoid memory leak */
- {
- free(quest);
- }
-
- /* if desired, put question back in list so student sees it again */
- if (math_opts->iopts[REPEAT_WRONGS])
- {
- int i;
- MC_MathQuestion* quest_copy;
- MC_MathQuestion* rand_loc;
-
- mcdprintf("\nAdding %d copies to question_list:", math_opts->iopts[COPIES_REPEATED_WRONGS]);
-
- /* can put in more than one copy (to drive the point home!) */
- for (i = 0; i < math_opts->iopts[COPIES_REPEATED_WRONGS]; i++)
- {
- quest_copy = create_node_from_card(fc);
- rand_loc = pick_random(quest_list_length, question_list);
- question_list = insert_node(question_list, rand_loc, quest_copy);
- quest_list_length++;
- }
- /* unanswered stays the same if a single copy recycled or */
- /* increases by 1 for each "extra" copy reinserted: */
- unanswered += (math_opts->iopts[COPIES_REPEATED_WRONGS] - 1);
- }
- else
- {
- mcdprintf("\nNot repeating wrong answers\n");
-
- /* not repeating questions so list gets shorter: */
- unanswered--;
- }
-
- #ifdef MC_DEBUG
- print_counters();
- printf("\nLeaving MC_NotAnswered_Correctly()\n");
- #endif
-
- return 1;
-
-}
-
-
-int MC_NotAnsweredCorrectly_id(int id)
-{
- MC_MathQuestion* mq;
- MC_FlashCard* fc;
-
- if(!active_quests)
- {
- mcdprintf("MC_NotAnsweredCorrectly_id() - active_quests is empty\n");
- return 0;
- }
- //Find the question with the given id, if it exists:
- //First take the question out of the active_quests list
- mq = active_quests;
- // Loop until mq is NULL or card found with matching id:
- while(mq && (id != mq->card.question_id))
- {
- mcdprintf("id is %d, mq->card.question_id is %d\n", id, mq->card.question_id);
- mq = mq->next;
- }
- if(!mq) // Means we didn't find matching card - something is wrong:
- {
- fprintf(stderr, "MC_NotAnsweredCorrectly_id() - matching question not found!\n");
- return 0;
- }
- //Now just pass address of card field to MC_NotAnsweredCorrectly():
- fc = &(mq->card);
- return MC_NotAnsweredCorrectly(fc);
-}
-
-
-
-/* Tells user interface if all questions have been answered correctly! */
-/* Requires that at list contained at least one question to start with */
-/* and that wrongly answered questions have been recycled. */
-int MC_MissionAccomplished(void)
-{
- if (starting_length
- && math_opts->iopts[REPEAT_WRONGS]
- && !unanswered)
- {
- return 1;
- }
- else
- {
- return 0;
- }
-}
-
-
-/* Returns number of questions left (either in list */
-/* or "in play") */
-int MC_TotalQuestionsLeft(void)
-{
- return unanswered;
-}
-
-/* Returns number of questions left in list, NOT */
-/* including questions currently "in play". */
-int MC_ListQuestionsLeft(void)
-{
- return quest_list_length;
-}
-
-
-/* Store the amount of time a given flashcard was */
-/* visible on the screen. Returns 1 if the request */
-/* succeeds, 0 otherwise. */
-int MC_AddTimeToList(float t)
-{
- int newsize = 0;
- float *newlist;
-
- /* This list will be allocated in an STL-like manner: when the */
- /* list gets full, allocate an additional amount of storage equal */
- /* to the current size of the list, so that only O(logN) allocations */
- /* will ever be needed. We therefore have to keep track of 2 sizes: */
- /* the allocated size, and the actual number of items currently on */
- /* the list. */
- if (length_time_per_question_list >= length_alloc_time_per_question_list) {
- /* The list is full, allocate more space */
- newsize = 2*length_time_per_question_list;
- if (newsize == 0)
- newsize = 100;
- newlist = realloc(time_per_question_list, newsize*sizeof(float));
- if (newlist == NULL) {
- #ifdef MC_DEBUG
- printf("\nError: allocation for time_per_question_list failed\n");
- #endif
- return 0;
- }
- time_per_question_list = newlist;
- length_alloc_time_per_question_list = newsize;
- }
-
- /* Append the time to the list */
- time_per_question_list[length_time_per_question_list++] = t;
- return 1;
-}
-
-/* Frees heap memory used in program: */
-void MC_EndGame(void)
-{
- delete_list(question_list);
- question_list = 0;
- delete_list(wrong_quests);
- wrong_quests = 0;
-
- if (math_opts)
- {
- free(math_opts);
- math_opts = 0;
- }
-
- free(time_per_question_list);
- time_per_question_list = NULL;
- length_alloc_time_per_question_list = 0;
- length_time_per_question_list = 0;
-
- initialized = 0;
-}
-
-
-
-/* prints struct to file */
-void MC_PrintMathOptions(FILE* fp, int verbose)
-{
- int i, vcommentsprimed = 0;
- //comments when writing out verbose...perhaps they can go somewhere less conspicuous
- static char* vcomments[NOPTS];
- if (!vcommentsprimed) //we only want to initialize these once
- {
- vcommentsprimed = 1;
- for (i = 0; i < NOPTS; ++i)
- vcomments[i] = NULL;
- vcomments[PLAY_THROUGH_LIST] =
- "\n############################################################\n"
- "# #\n"
- "# General Math Options #\n"
- "# #\n"
- "# If 'play_through_list' is true, Tuxmath will ask each #\n"
- "# question in an internally-generated list. The list is #\n"
- "# generated based on the question ranges selected below. #\n"
- "# The game ends when no questions remain. #\n"
- "# If 'play_through_list' is false, the game continues #\n"
- "# until all cities are destroyed. #\n"
- "# Default is 1 (i.e. 'true' or 'yes'). #\n"
- "# #\n"
- "# 'question_copies' is the number of times each question #\n"
- "# will be asked. It can be 1 to 10 - Default is 1. #\n"
- "# #\n"
- "# 'repeat_wrongs' tells Tuxmath whether to reinsert #\n"
- "# incorrectly answered questions into the list to be #\n"
- "# asked again. Default is 1 (yes). #\n"
- "# #\n"
- "# 'copies_repeated_wrongs' gives the number of times an #\n"
- "# incorrectly answered question will reappear. Default #\n"
- "# is 1. #\n"
- "# #\n"
- "# The defaults for these values result in a 'mission' #\n"
- "# for Tux that is accomplished by answering all #\n"
- "# questions correctly with at least one surviving city. #\n"
- "############################################################\n\n";
-
- vcomments[FORMAT_ADD_ANSWER_LAST] =
- "\n############################################################\n"
- "# The 'format_<op>_answer_<place> options control #\n"
- "# generation of questions with the answer in different #\n"
- "# places in the equation. i.e.: #\n"
- "# #\n"
- "# format_add_answer_last: 2 + 2 = ? #\n"
- "# format_add_answer_first: ? + 2 = 4 #\n"
- "# format_add_answer_middle: 2 + ? = 4 #\n"
- "# #\n"
- "# By default, 'format_answer_first' is enabled and the #\n"
- "# other two formats are disabled. Note that the options #\n"
- "# are not mutually exclusive - the question list may #\n"
- "# contain questions with different formats. #\n"
- "# #\n"
- "# The formats are set independently for each of the four #\n"
- "# math operations. #\n"
- "############################################################\n\n";
-
- vcomments[ALLOW_NEGATIVES] =
- "\n############################################################\n"
- "# 'allow_negatives' allows or disallows use of negative #\n"
- "# numbers as both operands and answers. Default is 0 #\n"
- "# (no), which disallows questions like: #\n"
- "# 2 - 4 = ? #\n"
- "# Note: this option must be enabled in order to set the #\n"
- "# operand ranges to include negatives (see below). If it #\n"
- "# is changed from 1 (yes) to 0 (no), any negative #\n"
- "# operand limits will be reset to 0. #\n"
- "############################################################\n\n";
-
- vcomments[MAX_ANSWER] =
- "\n############################################################\n"
- "# 'max_answer' is the largest absolute value allowed in #\n"
- "# any value in a question (not only the answer). Default #\n"
- "# is 144. It can be set as high as 999. #\n"
- "############################################################\n\n";
-
- vcomments[MAX_QUESTIONS] =
- "\n############################################################\n"
- "# 'max_questions' is limit of the length of the question #\n"
- "# list. Default is 5000 - only severe taskmasters will #\n"
- "# need to raise it. #\n"
- "############################################################\n\n";
-
- vcomments[RANDOMIZE] =
- "\n############################################################\n"
- "# If 'randomize' selected, the list will be shuffled #\n"
- "# at the start of the game. Default is 1 (yes). #\n"
- "############################################################\n\n";
-
- vcomments[ADDITION_ALLOWED] =
- "\n############################################################\n"
- "# #\n"
- "# Math Operations Allowed #\n"
- "# #\n"
- "# These options enable questions for each of the four math #\n"
- "# operations. All are 1 (yes) by default. #\n"
- "############################################################\n\n";
-
- vcomments[MIN_AUGEND] =
- "\n############################################################\n"
- "# #\n"
- "# Minimum and Maximum Values for Operand Ranges #\n"
- "# #\n"
- "# Operand limits can be set to any integer up to the #\n"
- "# value of 'max_answer'. If 'allow_negatives' is set to 1 #\n"
- "# (yes), either negative or positive values can be used. #\n"
- "# Tuxmath will generate questions for every value in the #\n"
- "# specified range. The maximum must be greater than or #\n"
- "# equal to the corresponding minimum for any questions to #\n"
- "# be generated for that operation. #\n"
- "############################################################\n\n";
-
- }
-
-
- mcdprintf("\nEntering MC_PrintMathOptions()\n");
-
- /* bail out if no struct */
- if (!math_opts)
- {
- fprintf(stderr, "\nMath Options struct does not exist!\n");
- return;
- }
-
- for (i = 0; i < NOPTS; ++i)
- {
- if (verbose && vcomments[i] != NULL)
- fprintf(fp, "%s", vcomments[i]);
- fprintf(fp, "%s = %d\n", MC_OPTION_TEXT[i], math_opts->iopts[i]);
- }
- mcdprintf("\nLeaving MC_PrintMathOptions()\n");
-}
-
-
-
-int MC_PrintQuestionList(FILE* fp)
-{
- if (fp && question_list)
- {
- print_list(fp, question_list);
- return 1;
- }
- else
- {
- fprintf(stderr, "\nFile pointer and/or question list invalid\n");
- return 0;
- }
-}
-
-int MC_PrintWrongList(FILE* fp)
-{
- if (!fp)
- {
- fprintf(stderr, "File pointer invalid\n");
- return 0;
- }
-
- if (wrong_quests)
- {
- print_list(fp, wrong_quests);
- }
- else
- {
- fprintf(fp, "\nNo wrong questions!\n");
- }
-
- return 1;
-}
-
-
-int MC_StartingListLength(void)
-{
- return starting_length;
-}
-
-
-int MC_WrongListLength(void)
-{
- return list_length(wrong_quests);
-}
-
-int MC_NumAnsweredCorrectly(void)
-{
- return answered_correctly;
-}
-
-
-int MC_NumNotAnsweredCorrectly(void)
-{
- return answered_wrong;
-}
-
-
-/* Report the median time per question */
-float MC_MedianTimePerQuestion(void)
-{
- if (length_time_per_question_list == 0)
- return 0;
-
- qsort(time_per_question_list,length_time_per_question_list,sizeof(float),floatCompare);
- return time_per_question_list[length_time_per_question_list/2];
-}
-
-
-
-
-/* Implementation of "private methods" - (cannot be called from outside
-of this file) */
-
-
-
-/* Resets negative values to zero - used when allow_negatives deselected. */
-void clear_negatives(void)
-{
- int i;
- for (i = MIN_AUGEND; i <= MAX_TYPING_NUM; ++i)
- if (math_opts->iopts[i]< 0)
- math_opts->iopts[i]= 0;
-}
-
-// /* this is used by generate_list to see if a possible question */
-// /* meets criteria to be added to the list or not: */
-// int validate_question(int n1, int n2, int n3)
-// {
-// /* make sure none of values exceeds max_answer using absolute */
-// /* value comparison: */
-// if (abs_value(n1) > abs_value(math_opts->iopts[MAX_ANSWER])
-// || abs_value(n2) > abs_value(math_opts->iopts[MAX_ANSWER])
-// || abs_value(n3) > abs_value(math_opts->iopts[MAX_ANSWER]))
-// {
-// return 0;
-// }
-// /* make sure none of values are negative if negatives not allowed: */
-// if (!math_opts->iopts[ALLOW_NEGATIVES])
-// {
-// if (n1 < 0 || n2 < 0 || n3 < 0)
-// {
-// return 0;
-// }
-// }
-// return 1;
-// }
-
-#if 0 //this code is probably on the way out...
-/* create a new node and return a pointer to it */
-MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f)
-{
- MC_MathQuestion* ptr = NULL;
-
- ptr = (MC_MathQuestion*)malloc(sizeof(MC_MathQuestion));
-
- if (!ptr)
- {
- fprintf(stderr, "create_node() - malloc() failed!\n");
- return NULL;
- }
-
- ptr->card = MC_AllocateFlashcard();
- ptr->next = NULL;
- ptr->previous = NULL;
-
- snprintf(ptr->card.formula_string, max_formula_size, "%d %c %d = ?",
- n1, op < MC_NUM_OPERS ? operchars[op] : '\0', n2);
- snprintf(ptr->card.answer_string, max_formula_size, "%d", ans);
- ptr->card.difficulty = 25 * (op + 1);
-
-
- /* ptr should now point to a properly constructed node: */
- return ptr;
-}
-#endif
-
-MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard)
-{
- MC_MathQuestion* ret = allocate_node();
- copy_card(flashcard, &(ret->card));
- return ret;
-}
-
-// /* FIXME take care of strings */
-// /* this one copies the contents, including pointers; both nodes must be allocated */
-// int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy)
-// {
-// if (!original)
-// {
-// fprintf(stderr, "\nIn copy_node(): invalid 'original' pointer arg.\n");
-// return 0;
-// }
-// if (!copy)
-// {
-// fprintf(stderr, "\nIn copy_node(): invalid 'copy' pointer arg.\n");
-// return 0;
-// }
-//
-// copy_card(&(original->card), &(copy->card) );
-//
-// copy->next = original->next;
-// copy->previous = original->previous;
-// copy->randomizer = original->randomizer;
-// return 1;
-// }
-
-
-
-
-/* this puts the node into the list AFTER the node pointed to by current */
-/* and returns a pointer to the top of the modified list */
-MC_MathQuestion* insert_node(MC_MathQuestion* first,
- MC_MathQuestion* current,
- MC_MathQuestion* new_node)
-{
- /* return pointer to list unchanged if new_node doesn't exist*/
- if (!new_node)
- return first;
- /* if current doesn't exist, new_node is first */
- if (!current)
- {
- new_node->previous = 0;
- new_node->next =0;
- first = new_node;
- return first;
- }
-
- if (current->next) /* avoid error if at end of list */
- current->next->previous = new_node;
- new_node->next = current->next;
- current->next = new_node;
- new_node->previous = current;
- return first;
-}
-
-
-
-/* adds the new node to the end of the list */
-MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node)
-{
- MC_MathQuestion* ptr;
- /* return pointer to list unchanged if new_node doesn't exist*/
- if (!new_node)
- {
- return list;
- }
-
- /* if list does not exist, new_node is the first (and only) node */
- if (!list)
- {
- return new_node;
- }
- /* otherwise, go to end of list */
- ptr = list;
- while (ptr->next)
- {
- ptr = ptr->next;
- }
-
- ptr->next = new_node;
- new_node->previous = ptr;
- new_node->next = 0;
- return list;
-}
-
-
-
-/* this takes the node out of the list but does not delete it */
-/* and returns a pointer to the top of the modified list */
-MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n)
-{
- if (!n || !first)
- return first;
- /* special case if first node being removed */
- if (n == first)
- first = first->next;
-
- if (n->previous)
- n->previous->next = n->next;
- if (n->next)
- n->next->previous = n->previous;
- n->previous = 0;
- n->next = 0;
- return first;
-}
-
-
-
-/* frees memory for entire list and returns null pointer */
-MC_MathQuestion* delete_list(MC_MathQuestion* list)
-{
- MC_MathQuestion* tmp_ptr;
- while (list)
- {
- tmp_ptr = list->next;
- free_node (list);
- list = tmp_ptr;
- }
- return list;
-}
-
-
-
-void print_list(FILE* fp, MC_MathQuestion* list)
-{
- if (!list)
- {
- fprintf(fp, "\nprint_list(): list empty or pointer invalid\n");
- return;
- }
-
- MC_MathQuestion* ptr = list;
- while (ptr)
- {
- fprintf(stderr, "%s\n", ptr->card.formula_string);
- ptr = ptr->next;
- }
-}
-
-
-void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length)
-{
- if (!vect)
- {
- fprintf(fp, "\nprint_vect_list(): list empty or pointer invalid\n");
- return;
- }
-
- int i = 0;
- mcdprintf("Entering print_vect_list()\n");
- for(i = 0; i < length; i++)
- fprintf(fp, "%s\n", vect[i]->card.formula_string);
-
- mcdprintf("Leaving print_vect_list()\n");
-}
-
-
-
-#ifdef MC_DEBUG
-void print_card(MC_FlashCard card)
-{
- printf("\nprint_card():\n");
- printf("question_id: %d\nformula_string: %s\nanswer_string: %s\ndifficulty: %d\n\n",
- card.question_id,
- card.formula_string,
- card.answer_string,
- card.difficulty);
-}
-
-/* This sends the values of all "global" counters and the */
-/* lengths of the question lists to stdout - for debugging */
-void print_counters(void)
-{
- printf("\nquest_list_length = \t%d", quest_list_length);
- printf("\nlist_length(question_list) = \t%d", list_length(question_list));
- printf("\nstarting_length = \t%d", starting_length);
- printf("\nunanswered = \t%d", unanswered);
- printf("\nanswered_correctly = \t%d", answered_correctly);
- printf("\nanswered_wrong = \t%d", answered_wrong);
- printf("\nlist_length(wrong_quests) = \t%d", list_length(wrong_quests));
- printf("\nquestions_pending = \t%d", questions_pending);
- printf("\nlist_length(active_quests) = \t%d", list_length(active_quests));
-}
-
-// /* a "copy constructor", so to speak */
-// /* FIXME should properly return newly allocated list if more than one node DSB */
-// MC_MathQuestion* create_node_copy(MC_MathQuestion* other)
-// {
-// MC_MathQuestion* ret = allocate_node();
-// if (ret)
-// copy_card(&(other->card), &(ret->card) );
-// return ret;
-// }
-//
-// /* FIXME take care of strings */
-//
-// MC_FlashCard create_card_from_node(MC_MathQuestion* node)
-// {
-// MC_FlashCard fc;
-// if (!node)
-// return DEFAULT_CARD;
-// fc = MC_AllocateFlashcard();
-// copy_card(&(node->card), &fc);
-// return fc;
-// }
-#endif
-
-
-
-
-int list_length(MC_MathQuestion* list)
-{
- int length = 0;
- while (list)
- {
- length++;
- list = list->next;
- }
- return length;
-}
-
-
-
-
-
-
-/* This is a new implementation written in an attempt to avoid */
-/* the O(n^2) performance problems seen with the old randomization */
-/* function. The list is created as a vector, but is for now still */
-/* made a linked list to minimize changes needed elsewhere. */
-/* The argument is a pointer to the top of the old list. This extra */
-/* level of indirection allows the list to be shuffled "in-place". */
-/* The function returns 1 if successful, 0 on errors. */
-
-static int randomize_list(MC_MathQuestion** old_list)
-{
- MC_MathQuestion* old_tmp = *old_list;
- MC_MathQuestion** tmp_vect = NULL;
-
- int i = 0;
- if (!old_list || !*old_list) //invalid/empty list
- return 0;
-
- int old_length = list_length(old_tmp);
-
- /* set random seed: */
- srand(time(0));
-
-
- /* Allocate vector and set ptrs to nodes in old list: */
-
- /* Allocate a list of pointers, not space for the nodes themselves: */
- tmp_vect = (MC_MathQuestion**)malloc(sizeof(MC_MathQuestion*) * old_length);
- /* Set each pointer in the vector to the corresponding node: */
- for (i = 0; i < old_length; i++)
- {
- tmp_vect[i] = old_tmp;
- tmp_vect[i]->randomizer = rand();
- old_tmp = old_tmp->next;
- }
-
- /* Now simply sort on 'tmp_vect[i]->randomizer' to shuffle list: */
- qsort(tmp_vect, old_length,
- sizeof(MC_MathQuestion*),
- comp_randomizer);
-
- /* Re-create pointers to provide linked-list functionality: */
- /* (stop at 'old_length-1' because we dereference tmp_vect[i+1]) */
- for(i = 0; i < old_length - 1; i++)
- {
- if (!tmp_vect[i])
- {
- fprintf(stderr, "Invalid pointer!\n");
- return 0;
- }
- tmp_vect[i]->next = tmp_vect[i+1];
- tmp_vect[i+1]->previous = tmp_vect[i];
- }
- /* Handle end cases: */
- tmp_vect[0]->previous = NULL;
- tmp_vect[old_length-1]->next = NULL;
-
- /* Now arrange for arg pointer to indirectly point to first element! */
- *old_list = tmp_vect[0];
- free(tmp_vect);
- return 1;
-}
-
-
-
-/* This is needed for qsort(): */
-int comp_randomizer (const void* a, const void* b)
-{
-
- int int1 = (*(const struct MC_MathQuestion **) a)->randomizer;
- int int2 = (*(const struct MC_MathQuestion **) b)->randomizer;
-
- if (int1 > int2)
- return 1;
- else if (int1 == int2)
- return 0;
- else
- return -1;
-}
-
-MC_MathQuestion* pick_random(int length, MC_MathQuestion* list)
-{
- int i;
- int rand_node;
-
- /* set random seed DSB */
- srand(time(0));
-
- /* if length is zero, get out to avoid divide-by-zero error */
- if (0 == length)
- {
- return list;
- }
-
- rand_node = rand() % length;
-
- for (i=1; i < rand_node; i++)
- {
- if (list)
- list = list->next;
- }
-
- return list;
-}
-
-/* compares fields other than pointers */
-int compare_node(MC_MathQuestion* first, MC_MathQuestion* other)
-{
- if (!first || !other)
- return 0;
- if (compare_card(&(first->card), &(first->card) ) ) //cards are equal
- return 1;
- else
- return 0;
-}
-
-/* check to see if list already contains an identical node */
-int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr)
-{
- if (!list || !ptr)
- return 0;
-
- while (list)
- {
- if (compare_node(list, ptr))
- return 1;
- list = list->next;
- }
- return 0;
-}
-
-// /* to prevent option settings in math_opts from getting set to */
-// /* values other than 0 or 1 */
-// int int_to_bool(int i)
-// {
-// if (i)
-// return 1;
-// else
-// return 0;
-// }
-
-// /* prevent values from getting into math_opts that are outside */
-// /* the range that can be handled by the program (i.e. more */
-// /* than three digits; also disallow negatives if that has been */
-// /* selected. */
-// int sane_value(int i)
-// {
-// if (i > MC_GLOBAL_MAX)
-// i = MC_GLOBAL_MAX;
-// else if (i < -MC_GLOBAL_MAX)
-// i = -MC_GLOBAL_MAX;
-//
-// if (i < 0
-// && math_opts
-// && !math_opts->iopts[ALLOW_NEGATIVES])
-// {
-// i = 0;
-// }
-//
-// return i;
-// }
-
-// int abs_value(int i)
-// {
-// if (i > 0)
-// return i;
-// else
-// return -i;
-// }
-
-int log10i(int i) //base 10 logarithm for ints
-{
- int j;
- for (j = 0; i > 0; i /= 10, ++j);
- return j;
-}
-
-/* Compares two floats (needed for sorting in MC_MedianTimePerQuestion) */
-int floatCompare(const void *v1,const void *v2)
-{
- float f1,f2;
-
- f1 = *((float *) v1);
- f2 = *((float *) v2);
-
- if (f1 < f2)
- return -1;
- else if (f1 > f2)
- return 1;
- else
- return 0;
-}
-
-
-
-/****************************************************
-Functions for new mathcards architecture
-****************************************************/
-
-void copy_card(const MC_FlashCard* src, MC_FlashCard* dest)
-{
- if (!src || !dest)
- return;
- mcdprintf("Copying '%s' to '%s', ", src->formula_string,dest->formula_string);
- mcdprintf("copying '%s' to '%s'\n", src->answer_string, dest->answer_string);
- strncpy(dest->formula_string, src->formula_string, MC_FORMULA_LEN);
- strncpy(dest->answer_string, src->answer_string, MC_ANSWER_LEN);
- mcdprintf("Card is: '%s', '%s'\n", dest->formula_string, dest->answer_string);
- dest->answer = src->answer;
- dest->difficulty = src->difficulty;
- dest->question_id = src->question_id;
-}
-
-void free_node(MC_MathQuestion* mq) //no, not that freenode.
-{
- if (!mq)
- return;
- MC_FreeFlashcard(&(mq->card) );
- free(mq);
-}
-
-MC_MathQuestion* allocate_node()
-{
- MC_MathQuestion* ret = NULL;
- ret = malloc(sizeof(MC_MathQuestion) );
- if (!ret)
- {
- printf("Could not allocate space for a new node!\n");
- return NULL;
- }
-
- ret->card = MC_AllocateFlashcard();
- ret->next = ret->previous = NULL;
-
- return ret;
-}
-
-/*
-The function that does the central dirty work pertaining to flashcard
-creation. Extensible to just about any kind of math problem, perhaps
-with the exception of those with multiple answers, such as "8 + 2 > ?"
-Simply specify how the problem is presented to the user, and the
-answer the game should look for, as strings.
-*/
-MC_FlashCard generate_random_flashcard(void)
-{
- int num;
- int length;
- MC_ProblemType pt;
- MC_FlashCard ret;
- static int generate_random_flashcard_id=0;
-
- generate_random_flashcard_id+=1;
- mcdprintf("Entering generate_random_flashcard()\n");
- mcdprintf("%d\n",generate_random_flashcard_id);
- do
- pt = rand() % MC_NUM_PTYPES;
- while ( (pt == MC_PT_TYPING && !MC_GetOpt(TYPING_PRACTICE_ALLOWED) ) ||
- (pt == MC_PT_ARITHMETIC && !MC_GetOpt(ADDITION_ALLOWED) &&
- !MC_GetOpt(SUBTRACTION_ALLOWED) &&
- !MC_GetOpt(MULTIPLICATION_ALLOWED) &&
- !MC_GetOpt(DIVISION_ALLOWED) ) ||
- (pt == MC_PT_COMPARISON && !MC_GetOpt(COMPARISON_ALLOWED) )
- );
-
- if (pt == MC_PT_TYPING) //typing practice
- {
- mcdprintf("Generating typing question\n");
- ret = MC_AllocateFlashcard();
- num = rand() % (MC_GetOpt(MAX_TYPING_NUM)-MC_GetOpt(MIN_TYPING_NUM) + 1)
- + MC_GetOpt(MIN_TYPING_NUM);
- snprintf(ret.formula_string, MC_FORMULA_LEN, "%d", num);
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", num);
- ret.answer = num;
- ret.difficulty = 10;
- ret.question_id=generate_random_flashcard_id;
- }
- else //if (pt == MC_PT_ARITHMETIC)
- {
- mcdprintf("Generating arithmetic question");
- length = rand() % (MC_GetOpt(MAX_FORMULA_NUMS) -
- MC_GetOpt(MIN_FORMULA_NUMS) + 1) //avoid div by 0
- + MC_GetOpt(MIN_FORMULA_NUMS);
- mcdprintf(" of length %d", length);
- ret = generate_random_ooo_card_of_length(length, 1);
- #ifdef MC_DEBUG
- print_card(ret);
- #endif
- }
- //TODO comparison problems (e.g. "6 ? 9", "<")
-
- mcdprintf("Exiting generate_random_flashcard()\n");
-
- return ret;
-}
-
-/*
-Recursively generate an order of operations problem. Hopefully this won't
-raise performance issues. Difficulty is calculated based on the length of
-the formula and on the operators used. Problems have a 'base' difficulty of
-1 for binary operations, 3 for 3 numbers, 6, 10, etc. Each operator adds to
-the score: 0, 1, 2, and 3 respectively for addition, subtraction,
-multiplication and division.If reformat is 0, FORMAT_ANS_LAST will be used,
-otherwise a format is chosen at random.
-*/
-MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat)
-{
- int format = 0;
- int r1 = 0;
- int r2 = 0;
- int ans = 0;
- char tempstr[MC_FORMULA_LEN];
- MC_FlashCard ret;
- MC_Operation op;
- static int id = 0;
-
- id += 1;
- mcdprintf(".");
- if (length > MAX_FORMULA_NUMS)
- return DEFAULT_CARD;
- if (length <= 2)
- {
- mcdprintf("\n");
- ret = MC_AllocateFlashcard();
- for (op = rand() % MC_NUM_OPERS; //pick a random operation
- MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
- op = rand() % MC_NUM_OPERS);
-
- mcdprintf("Operation is %c\n", operchars[op]);
- /*
- if (op == MC_OPER_ADD)
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
- r2 = rand() % (math_opts->iopts[MAX_ADDEND] - math_opts->iopts[MIN_ADDEND] + 1) + math_opts->iopts[MIN_ADDEND];
- ans = r1 + r2;
- }
- else if (op == MC_OPER_SUB)
- {
- r1 = rand() % (math_opts->iopts[MAX_MINUEND] - math_opts->iopts[MIN_MINUEND] + 1) + math_opts->iopts[MIN_MINUEND];
- r2 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
- ans = r1 - r2;
- }
- else if (op == MC_OPER_MULT)
- {
- r1 = rand() % (math_opts->iopts[MAX_MULTIPLIER] - math_opts->iopts[MIN_MULTIPLIER] + 1) + math_opts->iopts[MIN_MULTIPLIER];
- r2 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_MULTIPLICAND];
- ans = r1 * r2;
- }
- else if (op == MC_OPER_DIV)
- {
- ans = rand() % (math_opts->iopts[MAX_QUOTIENT] - math_opts->iopts[MIN_QUOTIENT] + 1) + math_opts->iopts[MIN_QUOTIENT];
- r2 = rand() % (math_opts->iopts[MAX_DIVISOR] - math_opts->iopts[MIN_DIVISOR] + 1) + math_opts->iopts[MIN_DIVISOR];
- if (r2 == 0)
- r2 = 1;
- r1 = ans * r2;
- }
- */
- if (op > MC_OPER_DIV || op < MC_OPER_ADD)
- {
- mcdprintf("Invalid operator: value %d\n", op);
- return DEFAULT_CARD;
- }
- //choose two numbers in the proper range and get their result
-
- else do
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND+4*op] - math_opts->iopts[MIN_AUGEND+4*op] + 1) + math_opts->iopts[MIN_AUGEND+4*op];
- r2 = rand() % (math_opts->iopts[MAX_ADDEND+4*op] - math_opts->iopts[MIN_ADDEND+4*op] + 1) + math_opts->iopts[MIN_ADDEND+4*op];
-
- if (op == MC_OPER_ADD)
- ans = r1 + r2;
- if (op == MC_OPER_SUB)
- ans = r1 - r2;
- if (op == MC_OPER_MULT)
- ans = r1 * r2;
- if (op == MC_OPER_DIV)
- {
- if (r2 == 0)
- r2 = 1;
- ret.difficulty = r1;
- r1 *= r2;
- ans = ret.difficulty;
- }
- } while ( (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES)) || ans > MC_GetOpt(MAX_ANSWER) );
-
-
- mcdprintf("Constructing answer_string\n");
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", ans);
- mcdprintf("Constructing formula_string\n");
- snprintf(ret.formula_string, MC_FORMULA_LEN, "%d %c %d",
- r1, operchars[op], r2);
- ret.answer = ans;
- ret.difficulty = op + 1;
-
- }
- else //recurse
- {
- ret = generate_random_ooo_card_of_length(length - 1, 0);
-
- if (strchr(ret.formula_string, '+') || strchr(ret.formula_string, '-') )
- {
- //if the expression has addition or subtraction, we can't assume that
- //introducing multiplication or division will produce a predictable
- //result, so we'll limit ourselves to more addition/subtraction
- for (op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB;
- MC_GetOpt(op + ADDITION_ALLOWED) == 0;
- op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB);
-
- }
- else
- {
- //the existing expression can be treated as a number in itself, so we
- //can do anything to it and be confident of the result.
- for (op = rand() % MC_NUM_OPERS; //pick a random operation
- MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
- op = rand() % MC_NUM_OPERS);
- }
- mcdprintf("Next operation is %c,", operchars[op]);
-
- //pick the next operand
- if (op == MC_OPER_ADD)
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
- ret.answer += r1;
- }
- else if (op == MC_OPER_SUB)
- {
- r1 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
- ret.answer -= r1;
- }
- else if (op == MC_OPER_MULT)
- {
- r1 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_AUGEND];
- ret.answer *= r1;
- }
- else if (op == MC_OPER_DIV)
- {
- r1 = find_divisor(ret.answer);
- ret.answer /= r1;
- }
- else
- {
- ; //invalid operator
- }
- mcdprintf(" operand is %d\n", r1);
- mcdprintf("Answer: %d\n", ret.answer);
-
- //next append or prepend the new number (might need optimization)
- if (op == MC_OPER_SUB || op == MC_OPER_DIV || //noncommutative, append only
- rand() % 2)
- {
- snprintf(tempstr, MC_FORMULA_LEN, "%s %c %d", //append
- ret.formula_string, operchars[op], r1);
- strncpy(ret.formula_string, tempstr, MC_FORMULA_LEN);
- }
- else //we're prepending
- {
- snprintf(tempstr, MC_FORMULA_LEN, "%d %c %s", //append
- r1, operchars[op], ret.formula_string);
- strncpy(ret.formula_string, tempstr, MC_FORMULA_LEN);
- }
-
- //finally update the answer and score
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", ret.answer);
- ret.difficulty += (length - 1) + op;
- }
-
- if (reformat)
- {
- mcdprintf("Reformatting...\n");
- do {
- format = rand() % MC_NUM_FORMATS;
- } while (!MC_GetOpt(FORMAT_ANSWER_LAST + format) &&
- !MC_GetOpt(FORMAT_ADD_ANSWER_LAST + op * 3 + format) );
-
- strncat(ret.formula_string, " = ?", MC_FORMULA_LEN - strlen(ret.formula_string) );
- reformat_arithmetic(&ret, format );
- }
- ret.question_id=id;
- return ret;
-}
-
-
-
-MC_MathQuestion* generate_list(void)
-{
- int i, j;
- int length = MC_GetOpt(AVG_LIST_LENGTH);
- int cl; //raw length
- double r1, r2, delta, var; //randomizers for list length
- MC_MathQuestion* list = NULL;
- MC_MathQuestion* end_of_list = NULL;
- MC_MathQuestion* tnode = NULL;
-
-#ifdef MC_DEBUG
- MC_PrintMathOptions(stdout, 0);
-#endif
-
- if (!(MC_GetOpt(ARITHMETIC_ALLOWED) ||
- MC_GetOpt(TYPING_PRACTICE_ALLOWED) ||
- MC_GetOpt(COMPARISON_ALLOWED) ) )
- return NULL;
-
- //FIXME - remind me, why are we doing this??
- //randomize list length by a "bell curve" centered on average
- if (length && MC_GetOpt(VARY_LIST_LENGTH) )
- {
- r1 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
- r2 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
- mcdprintf("Randoms chosen: %5f, %5f\n", r1, r2);
- delta = sqrt(-2 * log(r1) ) * cos(2 * PI_VAL * r2); //standard normal dist.
- var = length / 10.0; //variance
- delta = delta * var;
- mcdprintf("Delta of average is %5f\n", delta);
- length += delta;
- if (length < 0)
- length = 1; //just in case...
- }
-
- if (MC_GetOpt(COMPREHENSIVE)) //generate all
- {
- int num_valid_questions; //How many questions the COMPREHENSIVE list specifies
- int cycles_needed; //How many times we need to generate it to get enough
-
- num_valid_questions = calc_num_valid_questions();
- if(num_valid_questions == 0)
- {
- fprintf(stderr, "generate_list() - no valid questions\n");
- return NULL;
- }
-
- cycles_needed = length/num_valid_questions;
-
- if((cycles_needed * num_valid_questions) < length)
- cycles_needed++;
-
- mcdprintf("In generate_list() - COMPREHENSIVE method requested\n");
- mcdprintf("num_valid_questions = %d\t cycles_needed = %d\n",
- num_valid_questions, cycles_needed);
-
- for (i = MC_PT_TYPING; i < MC_NUM_PTYPES; ++i)
- {
- if (!MC_GetOpt(i + TYPING_PRACTICE_ALLOWED))
- continue;
- for (j = 0; j < cycles_needed; j++)
- list = add_all_valid(i, list, &end_of_list);
- }
-
-
- if (MC_GetOpt(RANDOMIZE) )
- {
- mcdprintf("Randomizing list\n");
- randomize_list(&list);
- }
-
- if (length)
- {
- cl = list_length(list);
- // NOTE this should no longer happen - we run the COMPREHENSIVE
- // generation until we have enough questions.
- if (length > cl) //if not enough questions, pad out with randoms
- {
- mcdprintf("Padding out list from %d to %d questions\n", cl, length);
- for (i = cl; i < length; ++i)
- {
- tnode = malloc(sizeof(MC_MathQuestion) );
- if(!tnode)
- {
- fprintf(stderr, "In generate_list() - allocation failed!\n");
- delete_list(list);
- return NULL;
- }
-
- tnode->card = generate_random_flashcard();
- list = insert_node(list, end_of_list, tnode);
- end_of_list = tnode;
-// mcdprintf("%d.", list_length(list) );
- }
- }
- else if (length < cl) //if too many questions, chop off tail end of list
- {
- mcdprintf("Cutting list to %d questions\n", length);
- end_of_list = find_node(list, length);
- delete_list(end_of_list->next);
- end_of_list->next = NULL;
- }
- }
- }
-
- /* Here we are just generating random questions, one at a */
- /* time until we have enough */
- else
- {
- mcdprintf("In generate_list() - COMPREHENSIVE method NOT requested\n");
-
- for (i = 0; i < length; ++i)
- {
- tnode = malloc(sizeof(MC_MathQuestion) );
- if(!tnode)
- {
- fprintf(stderr, "In generate_list() - allocation failed!\n");
- delete_list(list);
- return NULL;
- }
-
- tnode->card = generate_random_flashcard();
- list = insert_node(list, end_of_list, tnode);
- end_of_list = tnode;
- }
- }
- return list;
-}
-
-/* NOTE - returns 0 (i.e. "false") if *identical*, and */
-/* 1 (i.e. "true") if *different* - counterintuitive, */
-/* but same behavior as e.g. strcmp() */
-
-static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b)
-{
- if (strncmp(a->formula_string, b->formula_string, MC_FORMULA_LEN) )
- return 1;
- if (strncmp(a->answer_string, b->answer_string, MC_ANSWER_LEN) )
- return 1;
- if (a->answer != b->answer);
- return 1;
-
- return 0; //the cards are identical
-}
-
-/* Public functions */
-
-/* allocate space for an MC_Flashcard */
-MC_FlashCard MC_AllocateFlashcard(void)
-{
- MC_FlashCard ret;
-
-//NOTE strings now simply hard-coded to MC_FORMULA_LEN (= 40) and
-//MC_ANSWER_LEN (= 5) instead of tailoring them to save a few bytes - DSB
-// mcdprintf("Allocating %d + %d bytes for flashcard\n",
-// max_formula_size + 1, max_answer_size + 1);
-// ret.formula_string = malloc( (max_formula_size + 1) * sizeof(char));
-// ret.answer_string = malloc( (max_answer_size + 1) * sizeof(char));
-// if (!ret.formula_string || !ret.answer_string)
-// {
-// free(ret.formula_string);
-// free(ret.answer_string);
-// printf("Couldn't allocate space for a new flashcard!\n");
-// ret = DEFAULT_CARD;
-// }
- return ret;
-}
-
-//Now a no-op - MC_FlashCard no longer has dynamically allocated strings
-void MC_FreeFlashcard(MC_FlashCard* fc)
-{
- return;
-// if (!fc)
-// return;
-// // mcdprintf("Freeing formula_string\n");
-// if (fc->formula_string)
-// {
-// free(fc->formula_string);
-// fc->formula_string = NULL;
-// }
-// // mcdprintf("Freeing answer_string\n");
-// if (fc->answer_string)
-// {
-// free(fc->answer_string);
-// fc->answer_string = NULL;
-// }
-}
-
-unsigned int MC_MapTextToIndex(const char* text)
-{
- int i;
- for (i = 0; i < NOPTS; ++i)
- {
- if (!strcasecmp(text, MC_OPTION_TEXT[i]) )
- return i;
- }
- mcdprintf("'%s' isn't a math option\n", text);
- return NOT_VALID_OPTION;
-}
-
-
-//TODO more intuitive function names for access by index vs. by text
-void MC_SetOpt(unsigned int index, int val)
-{
- if (index >= NOPTS)
- {
- mcdprintf("Invalid math option index: %d\n", index);
- return;
- }
-
- /* Do some sanity checks before we throw val into the struct: */
- switch(index)
- {
- /* All the booleans must be 0 or 1: */
- case PLAY_THROUGH_LIST:
- case REPEAT_WRONGS:
- case ALLOW_NEGATIVES:
- case FORMAT_ANSWER_LAST:
- case FORMAT_ANSWER_FIRST:
- case FORMAT_ANSWER_MIDDLE:
- case FORMAT_ADD_ANSWER_LAST:
- case FORMAT_ADD_ANSWER_FIRST:
- case FORMAT_ADD_ANSWER_MIDDLE:
- case FORMAT_SUB_ANSWER_LAST:
- case FORMAT_SUB_ANSWER_FIRST:
- case FORMAT_SUB_ANSWER_MIDDLE:
- case FORMAT_MULT_ANSWER_LAST:
- case FORMAT_MULT_ANSWER_FIRST:
- case FORMAT_MULT_ANSWER_MIDDLE:
- case FORMAT_DIV_ANSWER_LAST:
- case FORMAT_DIV_ANSWER_FIRST:
- case FORMAT_DIV_ANSWER_MIDDLE:
- case ADDITION_ALLOWED:
- case SUBTRACTION_ALLOWED:
- case MULTIPLICATION_ALLOWED:
- case DIVISION_ALLOWED:
- case TYPING_PRACTICE_ALLOWED:
- case ARITHMETIC_ALLOWED:
- case COMPARISON_ALLOWED:
- case RANDOMIZE:
- case COMPREHENSIVE:
- case VARY_LIST_LENGTH:
- {
- /* Reset all non-zero values to one: */
- if(val)
- {
- if(val != 1)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to 1\n", MC_OPTION_TEXT[index], val);
- val = 1;
- }
- }
- break;
- }
-
- /* Parameters concerning numbers of questions */
- /* must be greater than or equal to zero: */
- /* TODO some additional checks would make sense */
- case QUESTION_COPIES:
- case COPIES_REPEATED_WRONGS:
- case MAX_QUESTIONS:
- case MAX_FORMULA_NUMS:
- case MIN_FORMULA_NUMS:
- case AVG_LIST_LENGTH:
- {
- /* Reset all negative values to zero: */
- if(val < 0)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to 0\n", MC_OPTION_TEXT[index], val);
- val = 0;
- }
- break;
- }
-
- /* Operand values - make sure they are in displayable range */
- /* i.e. -999 to 999 */
- case MAX_ANSWER:
- case MIN_AUGEND:
- case MAX_AUGEND:
- case MIN_ADDEND:
- case MAX_ADDEND:
- case MIN_MINUEND:
- case MAX_MINUEND:
- case MIN_SUBTRAHEND:
- case MAX_SUBTRAHEND:
- case MIN_MULTIPLIER:
- case MAX_MULTIPLIER:
- case MIN_MULTIPLICAND:
- case MAX_MULTIPLICAND:
- case MIN_DIVISOR:
- case MAX_DIVISOR:
- case MIN_QUOTIENT:
- case MAX_QUOTIENT:
- case MIN_TYPING_NUM:
- case MAX_TYPING_NUM:
- case MIN_COMPARATOR:
- case MAX_COMPARATOR:
- case MIN_COMPARISAND:
- case MAX_COMPARISAND:
- {
- if(val > MC_GLOBAL_MAX)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to %d\n", MC_OPTION_TEXT[index],
- val, MC_GLOBAL_MAX);
- val = MC_GLOBAL_MAX;
- }
-
- if(val < (0 - MC_GLOBAL_MAX))
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to %d\n", MC_OPTION_TEXT[index],
- val, (0 - MC_GLOBAL_MAX));
- val = (0 - MC_GLOBAL_MAX);
- }
-
- break;
- }
-
- default:
- fprintf(stderr, "Warning - in MC_SetOpt() - unrecognized index %d\n",
- index);
- }
- /* Should now be safe to put "sanitized" value into struct: */
- math_opts->iopts[index] = val;
-}
-
-void MC_SetOp(const char* param, int val)
-{
- MC_SetOpt(MC_MapTextToIndex(param), val);
-}
-
-int MC_GetOpt(unsigned int index)
-{
- if (index >= NOPTS)
- {
- mcdprintf("Invalid option index: %d\n", index);
- return MC_MATH_OPTS_INVALID;
- }
- if (!math_opts)
- {
- printf("Invalid options list!\n");
- return MC_MATH_OPTS_INVALID;
- }
- return math_opts->iopts[index];
-}
-
-int MC_GetOp(const char* param)
-{
- return MC_GetOpt(MC_MapTextToIndex(param) );
-}
-
-int MC_VerifyOptionListSane(void)
-{
- return strcmp(MC_OPTION_TEXT[NOPTS], "END_OF_OPTS") == 0;
-}
-
-int MC_MaxFormulaSize(void)
-{
- return MC_FORMULA_LEN;
-}
-
-int MC_MaxAnswerSize(void)
-{
- return MC_ANSWER_LEN;
-}
-
-void MC_ResetFlashCard(MC_FlashCard* fc)
-{
- if (!fc || !fc->formula_string || !fc->answer_string)
- return;
- strncpy(fc->formula_string, " ", MC_FORMULA_LEN);
- strncpy(fc->answer_string, " ", MC_ANSWER_LEN);
- fc->answer = 0;
- fc->difficulty = 0;
-}
-
-int MC_FlashCardGood(const MC_FlashCard* fc)
-{
- return fc && fc->formula_string && fc->answer_string;
-}
-
-int find_divisor(int a)
-{
- int div = 1; //the divisor to return
- int realisticpasses = 3; //reasonable time after which a minimum should be met
- int i;
- do
- for (i = 0; i < NPRIMES; ++i) //test each prime
- if (a % smallprimes[i] == 0) //if it is a prime factor,
- if (rand() % (i + 1) == 0) //maybe we'll keep it
- if (div * smallprimes[i] <= MC_GetOpt(MAX_DIVISOR) ) //if we can,
- div *= smallprimes[i]; //update our real divisor
- //keep going if the divisor is too small
- while (div < MC_GetOpt(MIN_DIVISOR) && --realisticpasses);
-
- return div;
-}
-
-
-//Computes (approximately) the number of questions that will be returned
-//by add_all_valid() as specified by the current options. This does not
-//take into account screening out of invalid questions, such
-//as divide-by-zero and questions like "0 x ? = 0".
-static int calc_num_valid_questions(void)
-{
- int total_questions = 0;
- int k = 0;
- //First add the number of typing questions
- if (MC_GetOpt(TYPING_PRACTICE_ALLOWED))
- total_questions += (MC_GetOpt(MAX_TYPING_NUM) - MC_GetOpt(MIN_TYPING_NUM));
-
- //Now add how many questions we will have for each operation:
- for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
- {
- int num_this_oper = 0;
- int formats_this_oper = 0;
-
- if (!MC_GetOpt(k + ADDITION_ALLOWED) )
- continue;
-
- //calculate number of ordered pairs of first and second operands:
- //note the "+ 1" is due to the ranges being inclusive
- num_this_oper = (MC_GetOpt(MAX_AUGEND + 4 * k) - MC_GetOpt(MIN_AUGEND + 4 * k) + 1)
- *
- (MC_GetOpt(MAX_ADDEND + 4 * k) - MC_GetOpt(MIN_ADDEND + 4 * k) + 1);
- //check what formats are allowed
- if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
- formats_this_oper++;
- if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3))
- formats_this_oper++;
- if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
- formats_this_oper++;
- //Get total of e.g. addition questions:
- num_this_oper *= formats_this_oper;
- //add to overall total:
- total_questions += num_this_oper;
- }
-
- //TODO will also need to count up the COMPARISON questions once
- //they are implemented
- {
- }
-
- mcdprintf("calc_num_valid_questions():\t%d\n", total_questions);
- return total_questions;
-}
-
-
-//NOTE end_of_list** needs to be doubly indirect because otherwise the end does not
-//get updated in the calling code
-//NOTE the difficulty is set as add = 1, sub = 2, mult = 3, div = 4, plus a 2 point
-//bonus if the format is a "missing number".
-MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list)
-{
- int i, j;
- int ans = 0, tmp;
- MC_Operation k;
- MC_MathQuestion* tnode;
-
- mcdprintf("Entering add_all_valid(%d)\n", pt);
- mcdprintf("List already has %d questions\n", list_length(list));
-
- //make sure this problem type is actually allowed
- if (!MC_GetOpt(pt + TYPING_PRACTICE_ALLOWED) )
- return list;
-
- //add all typing questions in range
- if (pt == MC_PT_TYPING)
- {
- mcdprintf("Adding typing...\n");
- for (i = MC_GetOpt(MIN_TYPING_NUM); i <= MC_GetOpt(MAX_TYPING_NUM); ++i)
- {
- mcdprintf("(%d)\n", i);
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN, "%d", i);
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", i);
- tnode->card.difficulty = 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- }
-
- //add all allowed arithmetic questions
- else if (MC_PT_ARITHMETIC)
- {
- mcdprintf("Adding arithmetic...\n");
-
- // The k loop iterates through the four arithmetic operations:
- // k = 0 means addition
- // k = 1 means subtraction
- // k = 2 means multiplication
- // k = 3 means division
- for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
- {
- if (!MC_GetOpt(k + ADDITION_ALLOWED) )
- continue;
- mcdprintf("\n*%d*\n", k);
-
- // The i loop iterates through the first value in the question:
- for (i = MC_GetOpt(MIN_AUGEND + 4 * k); i <= MC_GetOpt(MAX_AUGEND + 4 * k); ++i)
- {
- mcdprintf("\n%d:\n", i);
-
- // The j loop iterates through the second value in the question:
- for (j = MC_GetOpt(MIN_ADDEND + 4 * k); j <= MC_GetOpt(MAX_ADDEND + 4 * k); ++j)
- {
- // Generate the third number according to the operation.
- // Although it is called "ans", it will not be the actual
- // answer if it is a "missing number" type problem
- // (e.g. "3 x ? = 12")
- // We also filter out invalid questions here
- switch (k)
- {
- case MC_OPER_ADD:
- {
- ans = i + j;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_SUB:
- {
- ans = i - j;
- // throw out negatives if they aren't allowed:
- if (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES))
- continue;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_MULT:
- {
- ans = i * j;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_DIV:
- {
- // throw anything over MAX_ANSWER
- if (i * j > MC_GetOpt(MAX_ANSWER))
- continue;
-
- tmp = i;
- i *= j;
- ans = j;
- j = tmp;
- break;
- }
- default:
- fprintf(stderr, "Unrecognized operation type: %d\n", k);
- continue;
- }
-
- mcdprintf("Generating: %d %c %d = %d\n", i, operchars[k], j, ans);
-
- //add each format, provided it's allowed in general and for this op
-
- // Questions like "a + b = ?"
- if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
- {
- // Avoid division by zero:
- if (k == MC_OPER_DIV && j == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", ans);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "%d %c %d = ?", i, operchars[k], j);
- tnode->card.difficulty = k + 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
-
-
- // Questions like "? + b = c"
- if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3) )
- {
- // Avoid questions with indeterminate answer:
- // e.g. "? x 0 = 0"
- if (k == MC_OPER_MULT && j == 0)
- {
- continue;
- }
- // Avoid division by zero:
- if (k == MC_OPER_DIV && j == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", i);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "? %c %d = %d", operchars[k], j, ans);
- tnode->card.difficulty = k + 3;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
-
-
- // Questions like "a + ? = c"
- if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
- {
- // Avoid questions with indeterminate answer:
- // e.g. "0 x ? = 0"
- if (k == MC_OPER_MULT && i == 0)
- continue;
-
- // e.g. "0 / ? = 0"
- if (k == MC_OPER_DIV && i == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", j);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "%d %c ? = %d", i, operchars[k], ans);
- tnode->card.difficulty = k + 3;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- //If we divided, reset i and j so loop works correctly
- if (k == MC_OPER_DIV)
- {
- j = ans;
- i = tmp;
- mcdprintf("resetting to %d %d\n", j, i);
- }
- }
- }
- }
- }
- //add all comparison questions (TODO implement them!)
- else if (pt == MC_PT_COMPARISON)
- {
- for (i = MC_GetOpt(MIN_COMPARATOR); i < MC_GetOpt(MAX_COMPARATOR); ++i)
- {
- for (j = MC_GetOpt(MIN_COMPARISAND); j < MC_GetOpt(MAX_COMPARISAND); ++j)
- {
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN, "%d ? %d", i,j);
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN,
- i < j ? "<" :
- i > j ? ">" :
- "=");
- tnode->card.difficulty = 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- }
- }
- mcdprintf("Exiting add_all_valid()\n");
- mcdprintf("List now has %d questions\n\n", list_length(list));
-
- return list;
-}
-
-MC_MathQuestion* find_node(MC_MathQuestion* list, int num)
-{
- while (--num > 0 && list)
- list = list->next;
- return list;
-}
-
-void reformat_arithmetic(MC_FlashCard* card, MC_Format f)
-{
- int i, j;
- char* beg = 0;
- char* end = 0;
- char nans[MC_ANSWER_LEN];
- char nformula[MC_FORMULA_LEN + MC_ANSWER_LEN]; //gets a bit larger than usual in the meantime
-
- {
- //snprintf(nans, max_answer_size, "%s", card->answer_string);
-
- //insert old answer where question mark was
- for (i = 0, j = 0; card->formula_string[j] != '?'; ++i, ++j)
- nformula[i] = card->formula_string[j];
- i += snprintf(nformula + i, MC_ANSWER_LEN - 1, "%s", card->answer_string);
- snprintf(nformula + i, MC_FORMULA_LEN - i, "%s", card->formula_string + j + 1);
-
- //replace the new answer with a question mark
- if (f == MC_FORMAT_ANS_LAST)
- beg = strrchr(nformula, ' ') + 1;
- if (f == MC_FORMAT_ANS_FIRST)
- beg = nformula;
- if (f == MC_FORMAT_ANS_MIDDLE)
- beg = strchr(nformula, ' ') + 3;
- end = strchr(beg + 1, ' ');
- if (!end)
- end = "";
- //we now have beg = first digit of number to replace, end = the char after
- sscanf(beg, "%s", nans);
- *beg = 0; //sequester the first half of the string
- snprintf(card->formula_string, MC_FORMULA_LEN, "%s?%s", nformula, end);
- snprintf(card->answer_string, MC_ANSWER_LEN, "%s", nans);
- card->answer = atoi(card->answer_string);
- }
-}
Deleted: tuxmath/branches/lan/server/mathcards.h
===================================================================
--- tuxmath/branches/lan/server/mathcards.h 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/server/mathcards.h 2009-07-30 02:27:03 UTC (rev 1293)
@@ -1,278 +0,0 @@
-/*
-
- mathcards.h
-
- Description: contains headers for a flashcard-type math game.
- This is a sort of interface-independent backend that could be used with a different
- user interface. Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
- (aka tuxmath). If tuxmath were a C++ program, this would be a C++ class.
-
- Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2006
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-#ifndef MATHCARDS_H
-#define MATHCARDS_H
-
-//#define MC_DEBUG
-#ifdef MC_DEBUG
-#define mcdprintf(...) printf(__VA_ARGS__)
-#else
-#define mcdprintf(...) 0
-#endif
-
-
-/* different classes of problems TuxMath will ask */
-typedef enum _MC_ProblemType {
- MC_PT_TYPING,
- MC_PT_ARITHMETIC,
- MC_PT_COMPARISON,
- MC_NUM_PTYPES
-} MC_ProblemType;
-
-/* type of math operation used in an arithmetic question */
-typedef enum _MC_Operation {
- MC_OPER_ADD,
- MC_OPER_SUB,
- MC_OPER_MULT,
- MC_OPER_DIV,
- MC_NUM_OPERS
-} MC_Operation;
-
-/* math question formats: */
-typedef enum _MC_Format {
- MC_FORMAT_ANS_LAST, /* a + b = ? */
- MC_FORMAT_ANS_FIRST, /* ? + b = c */
- MC_FORMAT_ANS_MIDDLE, /* a + ? = c */
- MC_NUM_FORMATS
-} MC_Format;
-
-
-/*
-Indices for the various integer options. These are NOT the actual values!
-Actual values are accessed as such: options.iopts[PLAY_THROUGH_LIST] = val;
-Creating additional [integral] options is now centralized--it should only
-be necessary to add to this list, the list of text, and the list of
-defaults. (Besides actually using the new options!)
-*/
-enum {
- NOT_VALID_OPTION = -1 ,
- PLAY_THROUGH_LIST = 0 , /* play until all questions answered correctly */
- QUESTION_COPIES , /* # times each question is put in list */
- REPEAT_WRONGS , /* reuse incorrectly answered questions or not */
- COPIES_REPEATED_WRONGS , /* how many copies of an incorrectly answered question to re-insert*/
- ALLOW_NEGATIVES ,
- MAX_ANSWER ,
- MAX_QUESTIONS ,
- MAX_FORMULA_NUMS ,
- MIN_FORMULA_NUMS ,
-
- //NOTE: Do _not_ rearrange the FORMAT values because the functions
- //rely on index arithmetic to iterate through these, and will be
- //broken if the relative position changes!
- FORMAT_ANSWER_LAST , /* question format is: a + b = ? */
- FORMAT_ANSWER_FIRST , /* question format is: ? + b = c */
- FORMAT_ANSWER_MIDDLE , /* question format is: a + ? = c */
- FORMAT_ADD_ANSWER_LAST , /* a + b = ? */
- FORMAT_ADD_ANSWER_FIRST , /* ? + b = c */
- FORMAT_ADD_ANSWER_MIDDLE , /* a + ? = c */
- FORMAT_SUB_ANSWER_LAST , /* a - b = ? */
- FORMAT_SUB_ANSWER_FIRST , /* ? - b = c */
- FORMAT_SUB_ANSWER_MIDDLE , /* a - ? = c */
- FORMAT_MULT_ANSWER_LAST , /* a * b = ? */
- FORMAT_MULT_ANSWER_FIRST , /* ? * b = c */
- FORMAT_MULT_ANSWER_MIDDLE , /* a * ? = c */
- FORMAT_DIV_ANSWER_LAST , /* a / b = ? */
- FORMAT_DIV_ANSWER_FIRST , /* ? / b = c */
- FORMAT_DIV_ANSWER_MIDDLE , /* a / ? = c */
-
- ADDITION_ALLOWED ,
- SUBTRACTION_ALLOWED ,
- MULTIPLICATION_ALLOWED ,
- DIVISION_ALLOWED ,
- TYPING_PRACTICE_ALLOWED ,
- ARITHMETIC_ALLOWED ,
- COMPARISON_ALLOWED ,
-
- MIN_AUGEND , /* augend + addend = sum */
- MAX_AUGEND ,
- MIN_ADDEND ,
- MAX_ADDEND ,
-
- MIN_MINUEND , /* minuend - subtrahend = difference */
- MAX_MINUEND ,
- MIN_SUBTRAHEND ,
- MAX_SUBTRAHEND ,
-
- MIN_MULTIPLIER , /* multiplier * multiplicand = product */
- MAX_MULTIPLIER ,
- MIN_MULTIPLICAND ,
- MAX_MULTIPLICAND ,
-
- MIN_DIVISOR , /* dividend/divisor = quotient */
- MAX_DIVISOR , /* note - generate_list() will prevent */
- MIN_QUOTIENT , /* questions with division by zero. */
- MAX_QUOTIENT ,
-
- MIN_TYPING_NUM , /* range for "typing tutor" mode, for */
- MAX_TYPING_NUM , /* kids just learning to use keyboard. */
-
- MIN_COMPARATOR , /* left comparison operand */
- MAX_COMPARATOR ,
- MIN_COMPARISAND , /* right comparison operannd */
- MAX_COMPARISAND ,
-
- RANDOMIZE , /* whether to shuffle cards */
-
- COMPREHENSIVE , /* whether to generate all questions 'in order' */
- AVG_LIST_LENGTH , /* the average number of questions in a list */
- VARY_LIST_LENGTH , /* whether to randomly adjust list length */
-
- NOPTS
-};
-
-extern const char* const MC_OPTION_TEXT[];
-extern const int MC_DEFAULTS[];
-extern const char operchars[MC_NUM_OPERS];
-
-/* default values for math_options */
-#define MC_MAX_DIGITS 3
-#define MC_GLOBAL_MAX 999 /* This is the largest absolute value that */
- /* can be entered for math question values.*/
-#define MC_MATH_OPTS_INVALID -9999 /* Return value for accessor functions */
- /* if math_opts not valid */
-//#define DEFAULT_FRACTION_TO_KEEP 1
-
-
-typedef struct _MC_Options
-{
- int iopts[NOPTS];
-} MC_Options;
-
-
-
-/* struct for node in math "flashcard" list */
-typedef struct MC_MathQuestion {
- MC_FlashCard card;
- struct MC_MathQuestion* next;
- struct MC_MathQuestion* previous;
- int randomizer;
-} MC_MathQuestion;
-
-
-/* "public" function prototypes: these functions are how */
-/* a user interface communicates with MathCards: */
-/* TODO provide comments thoroughly explaining these functions */
-
-/* MC_Initialize() sets up the struct containing all of */
-/* settings regarding math questions. It should be */
-/* called before any other function. Many of the other */
-/* functions will not work properly if MC_Initialize() */
-/* has not been called. It only needs to be called once, */
-/* i.e when the program is starting, not at the beginning*/
-/* of each math game for the player. Returns 1 if */
-/* successful, 0 otherwise. */
-int MC_Initialize(void);
-
-/* MC_StartGame() generates the list of math questions */
-/* based on existing settings. It should be called at */
-/* the beginning of each math game for the player. */
-/* Returns 1 if resultant list contains 1 or more */
-/* questions, 0 if list empty or not generated */
-/* successfully. */
-int MC_StartGame(void);
-
-/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
-/* but uses the incorrectly answered questions from the */
-/* previous game for the question list as a review form */
-/* of learning. If there were no wrong answers (or no */
-/* previous game), it behaves just like MC_StartGame(). */
-/* FIXME wonder if it should generate a message if the */
-/* list is created from settings because there is no */
-/* valid wrong question list? */
-int MC_StartGameUsingWrongs(void);
-
-/* MC_NextQuestion() takes a pointer to an allocated */
-/* MC_MathQuestion struct and fills in the fields for */
-/* use by the user interface program. It basically is */
-/* like taking the next flashcard from the pile. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-/* or if argument pointer is invalid */
-int MC_NextQuestion(MC_FlashCard* q);
-
-/* MC_AnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has been answered */
-/* correctly. Returns 1 if no errors. */
-int MC_AnsweredCorrectly(MC_FlashCard* q);
-int MC_AnsweredCorrectly_id(int id);
-
-/* MC_NotAnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has not been */
-/* answered correctly. Returns 1 if no errors. */
-int MC_NotAnsweredCorrectly(MC_FlashCard* q);
-int MC_NotAnsweredCorrectly_id(int id);
-
-/* Like MC_NextQuestion(), but takes "flashcard" from */
-/* pile of incorrectly answered questions. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-int MC_NextWrongQuest(MC_FlashCard* q);
-
-/* Returns 1 if all have been answered correctly, */
-/* 0 otherwise. */
-int MC_MissionAccomplished(void);
-
-/* Returns number of questions left (either in list */
-/* or "in play") */
-int MC_TotalQuestionsLeft(void);
-
-/* Returns questions left in list, NOT */
-/* including questions currently "in play". */
-int MC_ListQuestionsLeft(void);
-
-/* To keep track of how long students take to answer the */
-/* questions, one can report the time needed to answer */
-/* an individual question: */
-int MC_AddTimeToList(float t);
-/* Note that initialization of the list is handled by */
-/* MC_StartGame. */
-
-/* Tells MathCards to clean up - should be called when */
-/* user interface program exits. */
-void MC_EndGame(void);
-
-/* Prints contents of math_opts struct in human-readable */
-/* form to given file. "verbose" tells the function to */
-/* write a lot of descriptive "help"-type info for each */
-/* option (intended to make config files self-documenting).*/
-void MC_PrintMathOptions(FILE* fp, int verbose);
-
-/* Additional functions used to generate game summary files: */
-int MC_PrintQuestionList(FILE* fp);
-int MC_PrintWrongList(FILE* fp);
-int MC_StartingListLength(void);
-int MC_WrongListLength(void);
-int MC_NumAnsweredCorrectly(void);
-int MC_NumNotAnsweredCorrectly(void);
-float MC_MedianTimePerQuestion(void);
-
-/********************************************
-Public functions for new mathcards architecture
-*********************************************/
-/* Return the array index of the given text, e.g. randomize->47 */
-unsigned int MC_MapTextToIndex(const char* text);
-void MC_SetOpt(unsigned int index, int val); //access directly,for internal use
-int MC_GetOpt(unsigned int index);
-void MC_SetOp(const char* param, int val); //access by text, for config reading
-int MC_GetOp(const char* param);
-int MC_VerifyOptionListSane(void);
-int MC_MaxFormulaSize(void); //amount of memory needed to safely hold strings
-int MC_MaxAnswerSize(void);
-MC_FlashCard MC_AllocateFlashcard();
-void MC_FreeFlashcard(MC_FlashCard* fc);
-void MC_ResetFlashCard(MC_FlashCard* fc); //empty flashcard of strings & values
-int MC_FlashCardGood(const MC_FlashCard* fc); //verifies a flashcard is valid
-/* Reorganize formula_string and answer_string to render the same equation
- in a different format */
-void reformat_arithmetic(MC_FlashCard* card, MC_Format f);
-#endif
Modified: tuxmath/branches/lan/server/server.c
===================================================================
--- tuxmath/branches/lan/server/server.c 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/server/server.c 2009-07-30 02:27:03 UTC (rev 1293)
@@ -24,8 +24,8 @@
#include <string.h>
#include "server.h"
-#include "transtruct.h"
-#include "mathcards.h"
+#include "../src/transtruct.h"
+#include "../src/mathcards.h"
#define MAX_CLIENTS 16
@@ -52,7 +52,7 @@
int handle_client_game_msg(int i, char* buffer);
void handle_client_nongame_msg(int i, char* buffer);
int msg_set_name(int i, char* buf);
-void start_game(int i);
+void start_game(void);
void game_msg_correct_answer(int i, char* inbuf);
void game_msg_wrong_answer(int i, char* inbuf);
void game_msg_quit(int i);
@@ -62,7 +62,7 @@
int send_counter_updates(void);
int add_question(MC_FlashCard* fc);
int remove_question(int id);
-int SendQuestion(MC_FlashCard flash, TCPsocket client_sock);
+//int SendQuestion(MC_FlashCard flash, TCPsocket client_sock);
int SendMessage(int message, int ques_id, char* name, TCPsocket client_sock);
int player_msg(int i, char* msg);
void broadcast_msg(char* msg);
@@ -70,10 +70,8 @@
int transmit_all(char* msg);
//Deprecated:
-void test_connections(void);
-void ping_client(int i);
-int no_questions_left(void);
-int mission_accomplished(void);
+// void test_connections(void);
+// void ping_client(int i);
// not really deprecated but not done in response to
// client message --needs better name:
@@ -568,7 +566,7 @@
}
}
if(someone_connected && !someone_not_ready)
- game_in_progress = 1;
+ start_game();
}
}
@@ -580,12 +578,17 @@
int x;
if(strncmp(buffer, "START_GAME", strlen("START_GAME")) == 0)
{
- start_game(i);
+ snprintf(buf, NET_BUF_LEN,
+ "Player %s ready to start math game",
+ client[i].name);
+ broadcast_msg(buf);
client[i].game_ready = 1;
- snprintf(buf, NET_BUF_LEN,
- "%s",
- "Success");
- x = SDLNet_TCP_Send(client[i].sock, buf, NET_BUF_LEN);
+ //This will call start_game() if all the other clients are ready:
+ check_game_clients();
+// snprintf(buf, NET_BUF_LEN,
+// "%s",
+// "Success");
+// x = SDLNet_TCP_Send(client[i].sock, buf, NET_BUF_LEN);
}
else if(strncmp(buffer, "SET_NAME", strlen("SET_NAME")) == 0)
@@ -734,15 +737,14 @@
void game_msg_next_question(void)
{
-
- int n;
- if (!MC_NextQuestion(&flash))
+ MC_FlashCard fc;
+ if (!MC_NextQuestion(&fc))
{
/* no more questions available */
printf("MC_NextQuestion() returned NULL - no questions available\n");
+ return;
}
- else
- {
+
#ifdef LAN_DEBUG
printf("WILL SEND >>\n");
printf("QUESTION_ID : %d\n", flash.question_id);
@@ -751,9 +753,11 @@
printf("ANSWER : %d\n",flash.answer);
printf("DIFFICULTY : %d\n",flash.difficulty);
#endif
- }
+
+
+ add_question(&fc);
- for(n = 0; n < MAX_CLIENTS && client[n].sock; n++)
+/* for(n = 0; n < MAX_CLIENTS && client[n].sock; n++)
{
#ifdef LAN_DEBUG
printf("About to send next question to client[%d]\n", n);
@@ -762,7 +766,7 @@
{
printf("Unable to send Question\n");
}
- }
+ } */
}
@@ -786,18 +790,20 @@
}
-void start_game(int i)
+
+
+
+
+/* Now this gets called to actually start the game once all the players */
+/* have indicated that they are ready: */
+void start_game(void)
{
char buf[NET_BUF_LEN];
char buffer[NET_BUF_LEN];
int x,j;
- snprintf(buf, NET_BUF_LEN,
- "Player %s added for next math game",
- client[i].name);
- broadcast_msg(buf);
- client[i].game_ready = 1; // Means this player is ready to start game
+ /* NOTE this should no longer be ready
/*This loop sees that the game starts only when all the players are ready */
/* i.e. if someone is connected but not ready, we return. */
for(j = 0; j < MAX_CLIENTS; j++)
@@ -806,7 +812,8 @@
if((client[j].game_ready != 1)
&& (client[j].sock != NULL))
{
- return;
+ printf("Warning - start_game() entered when someone not ready\n");
+ return;
}
}
@@ -845,6 +852,7 @@
/* Send enough questions to fill the initial comet slots (currently 10) */
for(j = 0; j < TEST_COMETS; j++)
{
+
int k = 0;
if (!MC_NextQuestion(&flash))
@@ -853,8 +861,7 @@
printf("MC_NextQuestion() returned NULL - no questions available\n");
return;
}
- else
- {
+
#ifdef LAN_DEBUG
printf("WILL SEND >>\n");
printf("QUESTION_ID : %d\n", flash.question_id);
@@ -863,9 +870,11 @@
printf("ANSWER : %d\n",flash.answer);
printf("DIFFICULTY : %d\n",flash.difficulty);
#endif
- }
- //Must send to all clients because client set will become discontinuous
+ //Now using add_question();
+ add_question(&flash);
+
+/* //Must send to all clients because client set will become discontinuous
//if anyone disconnects. SendQuestion() now returns harmlessly if
//the sock is NULL - DSB
for(k = 0; k < MAX_CLIENTS; k++)
@@ -875,59 +884,16 @@
{
printf("Unable to send Question to %s\n", client[k].name);
}
- }
+ }*/
}
+
//Send all the clients the counter totals:
send_counter_updates();
}
-int no_questions_left(void)
-{
- int x,j;
- char buf[NET_BUF_LEN];
- snprintf(buf, NET_BUF_LEN,
- "%s\n",
- "GAME_OVER_OTHER");
- for(j = 0; j < num_clients; j++)
- x = SDLNet_TCP_Send(client[j].sock, buf, sizeof(buf));
-
-#ifdef LAN_DEBUG
- printf("SendQuestion() - buf sent:::: %d bytes\n", x);
- printf("buf is: %s\n", buf);
-#endif
-
- if (x == 0)
- return 0;
- return 1;
-}
-
-
-
-int mission_accomplished(void)
-{
- int x,j;
-
- char buf[NET_BUF_LEN];
- snprintf(buf, NET_BUF_LEN,
- "%s\n",
- "GAME_OVER_WON");
- for(j = 0; j < num_clients; j++)
- x = SDLNet_TCP_Send(client[j].sock, buf, sizeof(buf));
-
-#ifdef LAN_DEBUG
- printf("SendQuestion() - buf sent:::: %d bytes\n", x);
- printf("buf is: %s\n", buf);
-#endif
-
- if (x == 0)
- return 0;
- return 1;
-}
-
-
//More centralized function to update the clients of the number of
//questions remaining, whether the mission has been accomplished,
//and so forth:
@@ -953,7 +919,7 @@
return 1;
}
-
+/* Sends a new question to all clients: */
int add_question(MC_FlashCard* fc)
{
char buf[NET_BUF_LEN];
@@ -972,7 +938,7 @@
return 1;
}
-
+/* Tells all clients to remove a specific question: */
int remove_question(int id)
{
char buf[NET_BUF_LEN];
@@ -988,36 +954,36 @@
//function to send a flashcard(question) from the server to the client
-int SendQuestion(MC_FlashCard flash, TCPsocket client_sock)
-{
- int x;
+// int SendQuestion(MC_FlashCard flash, TCPsocket client_sock)
+// {
+// int x;
+//
+// char buf[NET_BUF_LEN];
+//
+// if(client_sock == NULL)
+// return 0;
+//
+// snprintf(buf, NET_BUF_LEN,
+// "%s\t%d\t%d\t%d\t%s\t%s\n",
+// "SEND_QUESTION",
+// flash.question_id,
+// flash.difficulty,
+// flash.answer,
+// flash.answer_string,
+// flash.formula_string);
+// x = SDLNet_TCP_Send(client_sock, buf, sizeof(buf));
+//
+// #ifdef LAN_DEBUG
+// printf("SendQuestion() - buf sent:::: %d bytes\n", x);
+// printf("buf is: %s\n", buf);
+// #endif
+//
+// if (x == 0)
+// return 0;
+// return 1;
+// }
- char buf[NET_BUF_LEN];
- if(client_sock == NULL)
- return 0;
-
- snprintf(buf, NET_BUF_LEN,
- "%s\t%d\t%d\t%d\t%s\t%s\n",
- "SEND_QUESTION",
- flash.question_id,
- flash.difficulty,
- flash.answer,
- flash.answer_string,
- flash.formula_string);
- x = SDLNet_TCP_Send(client_sock, buf, sizeof(buf));
-
-#ifdef LAN_DEBUG
- printf("SendQuestion() - buf sent:::: %d bytes\n", x);
- printf("buf is: %s\n", buf);
-#endif
-
- if (x == 0)
- return 0;
- return 1;
-}
-
-
/*Function to send any messages to the client be it any warnings
or anything the client is made to be informed */
int SendMessage(int message, int ques_id, char *name, TCPsocket client_sock)
@@ -1157,15 +1123,15 @@
// Go through and test all the current connections, removing
// any clients that fail to respond:
-void test_connections(void)
-{
- int i = 0;
+// void test_connections(void)
+// {
+// int i = 0;
+//
+// for (i = 0; i < MAX_CLIENTS; i++)
+// ping_client(i);
+// }
- for (i = 0; i < MAX_CLIENTS; i++)
- ping_client(i);
-}
-
// This is supposed to be a way to test and see if each client
// is really connected.
// FIXME I think we need to put in a SDLNet_TCP_Recv() to see
@@ -1173,34 +1139,34 @@
// PING_BACK. I am worried, however, that we could have a problem
// with intercepting messages not related to the ping testing - DSB
-void ping_client(int i)
-{
- char buf[NET_BUF_LEN];
- char msg[NET_BUF_LEN];
- int x;
-
- if(i < 0 || i > MAX_CLIENTS)
- {
- printf("ping_client() - invalid index argument\n");
- return;
- }
-
- if(client[i].sock == NULL)
- {
- return;
- }
-
-// sprintf(msg,"%s", "PING\n");
-// snprintf(buf, NET_BUF_LEN, "%s\t%s\n", "SEND_MESSAGE", msg);
- snprintf(buf, NET_BUF_LEN, "%s\n", "PING");
- x = SDLNet_TCP_Send(client[i].sock, buf, NET_BUF_LEN);
- if(x < NET_BUF_LEN)
- {
- printf("The client %s is disconnected\n",client[i].name);
- remove_client(i);
- }
-//#ifdef LAN_DEBUG
- printf("buf is: %s\n", buf);
- printf("SendMessage() - buf sent:::: %d bytes\n", x);
-//#endif
-}
+// void ping_client(int i)
+// {
+// char buf[NET_BUF_LEN];
+// char msg[NET_BUF_LEN];
+// int x;
+//
+// if(i < 0 || i > MAX_CLIENTS)
+// {
+// printf("ping_client() - invalid index argument\n");
+// return;
+// }
+//
+// if(client[i].sock == NULL)
+// {
+// return;
+// }
+//
+// // sprintf(msg,"%s", "PING\n");
+// // snprintf(buf, NET_BUF_LEN, "%s\t%s\n", "SEND_MESSAGE", msg);
+// snprintf(buf, NET_BUF_LEN, "%s\n", "PING");
+// x = SDLNet_TCP_Send(client[i].sock, buf, NET_BUF_LEN);
+// if(x < NET_BUF_LEN)
+// {
+// printf("The client %s is disconnected\n",client[i].name);
+// remove_client(i);
+// }
+// //#ifdef LAN_DEBUG
+// printf("buf is: %s\n", buf);
+// printf("SendMessage() - buf sent:::: %d bytes\n", x);
+// //#endif
+// }
Modified: tuxmath/branches/lan/server/testclient.c
===================================================================
--- tuxmath/branches/lan/server/testclient.c 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/server/testclient.c 2009-07-30 02:27:03 UTC (rev 1293)
@@ -25,7 +25,7 @@
//#include "SDL_net.h"
#include "../src/transtruct.h"
-#include "mathcards.h"
+#include "../src/mathcards.h"
#include "testclient.h"
#include "../src/throttle.h"
#include "../src/network.h"
@@ -90,12 +90,9 @@
printf("connected\n");
}
-<<<<<<< .mine
- else // More than one server - will have to get player selection
-=======
+
else // More than one server - will have to get player selection:
->>>>>>> .r1280
{
while(server_number < 0 || server_number >= servers_found)
{
Deleted: tuxmath/branches/lan/server/transtruct.h
===================================================================
--- tuxmath/branches/lan/server/transtruct.h 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/server/transtruct.h 2009-07-30 02:27:03 UTC (rev 1293)
@@ -1,53 +0,0 @@
-/*
-
- transtruct.h
-
- Description: contains headers for the data structures
- that would be transferred between the server and the client
- during the multiplayer LAN game.
-
- Author: David Bruce, Akash Gangil and the TuxMath team, (C) 2009
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-#ifndef TRANSTRUCT_H
-#define TRANSTRUCT_H
-
-#define LAN_DEBUG
-#define NET_BUF_LEN 512
-#define DEFAULT_PORT 4779
-#define NAME_SIZE 50
-#define MAX_SERVERS 50
-
-#define MC_USE_NEWARC
-#define MC_FORMULA_LEN 40
-#define MC_ANSWER_LEN 5
-
-#define TEST_COMETS 10
-
-
-#ifndef MC_USE_NEWARC
-/* struct for individual "flashcard" */
-typedef struct MC_FlashCard {
- int num1;
- int num2;
- int num3;
- int operation;
- int format;
- char formula_string[MC_FORMULA_LEN];
- char answer_string[MC_ANSWER_LEN];
-} MC_FlashCard;
-#else
-/* experimental struct for a more generalized flashcard */
-typedef struct _MC_FlashCard {
- char formula_string[MC_FORMULA_LEN];
- char answer_string[MC_ANSWER_LEN];
- int question_id;
- int answer;
- int difficulty;
-} MC_FlashCard;
-#endif
-
-
-#endif
Deleted: tuxmath/branches/lan/src/mathcards.c
===================================================================
--- tuxmath/branches/lan/src/mathcards.c 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/src/mathcards.c 2009-07-30 02:27:03 UTC (rev 1293)
@@ -1,2492 +0,0 @@
-/*
-* C Implementation: mathcards.c
-*
-* Description: implementation of backend for a flashcard-type math game.
- Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
- (aka tuxmath). (If tuxmath were a C++ program, this would be a C++ class).
- MathCards could be used as the basis for similar games using a different interface.
-
-*
-*
-* Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2005
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*
-* Revised extensively in 2008 by Brendan Luchen, Tim Holy, and David Bruce
-*
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-
-
-#include "transtruct.h"
-#include "mathcards.h"
-
-/* extern'd constants */
-
-const char* const MC_OPTION_TEXT[NOPTS+1] = {
-"PLAY_THROUGH_LIST",
-"QUESTION_COPIES",
-"REPEAT_WRONGS",
-"COPIES_REPEATED_WRONGS",
-"ALLOW_NEGATIVES",
-"MAX_ANSWER",
-"MAX_QUESTIONS",
-"MAX_FORMULA_NUMS",
-"MIN_FORMULA_NUMS",
-
-"FORMAT_ANSWER_LAST",
-"FORMAT_ANSWER_FIRST",
-"FORMAT_ANSWER_MIDDLE",
-"FORMAT_ADD_ANSWER_LAST",
-"FORMAT_ADD_ANSWER_FIRST",
-"FORMAT_ADD_ANSWER_MIDDLE",
-"FORMAT_SUB_ANSWER_LAST",
-"FORMAT_SUB_ANSWER_FIRST",
-"FORMAT_SUB_ANSWER_MIDDLE",
-"FORMAT_MULT_ANSWER_LAST",
-"FORMAT_MULT_ANSWER_FIRST",
-"FORMAT_MULT_ANSWER_MIDDLE",
-"FORMAT_DIV_ANSWER_LAST",
-"FORMAT_DIV_ANSWER_FIRST",
-"FORMAT_DIV_ANSWER_MIDDLE",
-
-"ADDITION_ALLOWED",
-"SUBTRACTION_ALLOWED",
-"MULTIPLICATION_ALLOWED",
-"DIVISION_ALLOWED",
-"TYPING_PRACTICE_ALLOWED",
-"ARITHMETIC_ALLOWED",
-"COMPARISON_ALLOWED",
-
-"MIN_AUGEND",
-"MAX_AUGEND",
-"MIN_ADDEND",
-"MAX_ADDEND",
-
-"MIN_MINUEND",
-"MAX_MINUEND",
-"MIN_SUBTRAHEND",
-"MAX_SUBTRAHEND",
-
-"MIN_MULTIPLIER",
-"MAX_MULTIPLIER",
-"MIN_MULTIPLICAND",
-"MAX_MULTIPLICAND",
-
-"MIN_DIVISOR",
-"MAX_DIVISOR",
-"MIN_QUOTIENT",
-"MAX_QUOTIENT",
-
-"MIN_TYPING_NUM",
-"MAX_TYPING_NUM",
-
-"MIN_COMPARATOR" ,
-"MAX_COMPARATOR" ,
-"MIN_COMPARISAND",
-"MAX_COMPARISAND",
-
-"RANDOMIZE",
-
-"COMPREHENSIVE",
-"AVG_LIST_LENGTH",
-"VARY_LIST_LENGTH",
-
-"END_OF_OPTS"
-};
-
-
-
-const int MC_DEFAULTS[] = {
- 1, //PLAY_THROUGH_LIST
- 1, //QUESTION_COPIES
- 1, //REPEAT_WRONGS
- 1, //COPIES_REPEATED_WRONGS
- 0, //ALLOW_NEGATIVES
- 999, //MAX_ANSWER
- 5000, //MAX_QUESTIONS
- 2, //MAX_FORMULA_NUMS
- 2, //MIN_FORMULA_NUMS
- //
- 1, //FORMAT_ANSWER_LAST
- 0, //FORMAT_ANSWER_FIRST
- 0, //FORMAT_ANSWER_MIDDLE
- 1, //FORMAT_ADD_ANSWER_LAST
- 0, //FORMAT_ADD_ANSWER_FIRST
- 0, //FORMAT_ADD_ANSWER_MIDDLE
- 1, //FORMAT_SUB_ANSWER_LAST
- 0, //FORMAT_SUB_ANSWER_FIRST
- 0, //FORMAT_SUB_ANSWER_MIDDLE
- 1, //FORMAT_MULT_ANSWER_LAST
- 0, //FORMAT_MULT_ANSWER_FIRST
- 0, //FORMAT_MULT_ANSWER_MIDDLE
- 1, //FORMAT_DIV_ANSWER_LAST
- 0, //FORMAT_DIV_ANSWER_FIRST
- 0, //FORMAT_DIV_ANSWER_MIDDLE
- //
- 1, //ADDITION_ALLOWED
- 1, //SUBTRACTION_ALLOWED
- 1, //MULTIPLICATION_ALLOWED
- 1, //DIVISION_ALLOWED
-
- 0, //TYPING_PRACTICE_ALLOWED
- 1, //ARITHMETIC_ALLOWED
- 0, //COMPARISON_ALLOWED
- //
- 0, //MIN_AUGEND
- 12, //MAX_AUGEND
- 0, //MIN_ADDEND
- 12, //MAX_ADDEND
- //
- 0, //MIN_MINUEND
- 12, //MAX_MINUEND
- 0, //MIN_SUBTRAHEND
- 12, //MAX_SUBTRAHEND
- //
- 0, //MIN_MULTIPLIER
- 12, //MAX_MULTIPLIER
- 0, //MIN_MULTIPLICAND
- 12, //MAX_MULTIPLICAND
- //
- 0, //MIN_DIVISOR
- 12, //MAX_DIVISOR
- 0, //MIN_QUOTIENT
- 12, //MAX_QUOTIENT
- //
- 0, //MIN_TYPING_NUM
- 12, //MAX_TYPING_NUM
- //
- 0, //MIN_COMPARATOR
- 12, //MAX_COMPARATOR
- 0, //MIN_COMPARISAND
- 12, //MAX_COMPARISAND
-
- 1, //RANDOMIZE
-
- 0, //COMPREHENSIVE
- 100, //AVG_LIST_LENGTH
- 1 //VARY_LIST_LENGTH
-};
-
-
-
-/* "Globals" for mathcards.c: */
-#define PI_VAL 3.1415927
-#define NPRIMES 9
-const int smallprimes[NPRIMES] = {2, 3, 5 ,7, 11, 13, 17, 19, 23};
-const char operchars[4] = "+-*/";
-extern int n;
-
-MC_Options* math_opts = 0;
-MC_MathQuestion* question_list = 0;
-MC_MathQuestion* wrong_quests = 0;
-MC_MathQuestion* active_quests = 0;
-MC_MathQuestion* next_wrong_quest = 0;
-int initialized = 0;
-int quest_list_length = 0;
-int answered_correctly = 0;
-int answered_wrong = 0;
-int questions_pending = 0;
-int unanswered = 0;
-int starting_length = 0;
-//NOTE these are no longer used:
-int max_formula_size = 0; //max length in chars of a flashcard's formula
-int max_answer_size = 0; //and of its answer
-
-/* For keeping track of timing data */
-/*FIXME do we really need any of these? */
-float* time_per_question_list = NULL;
-int length_time_per_question_list = 0;
-int length_alloc_time_per_question_list = 0;
-
-const MC_FlashCard DEFAULT_CARD = {0,0,0,0}; //empty card to signal error
-
-/* "private" function prototypes: */
-/* */
-/* these are for internal use by MathCards only - like */
-/* the private functions of a C++ class. Declared static */
-/* to give file scope rather than extern scope. */
-
-static MC_MathQuestion* generate_list(void);
-static void clear_negatives(void);
-//static int validate_question(int n1, int n2, int n3);
-//static MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f);
-static MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard);
-static MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node);
-static MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node);
-static MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n);
-static MC_MathQuestion* delete_list(MC_MathQuestion* list);
-//static int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy);
-static int list_length(MC_MathQuestion* list);
-static int randomize_list(MC_MathQuestion** list);
-
-int comp_randomizer(const void *a, const void *b);
-static MC_MathQuestion* pick_random(int length, MC_MathQuestion* list);
-static int compare_node(MC_MathQuestion* first, MC_MathQuestion* other);
-static int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr);
-//static int int_to_bool(int i);
-//static int sane_value(int i);
-//static int abs_value(int i);
-static int log10i(int i);
-static int floatCompare(const void *v1,const void *v2);
-
-static void print_list(FILE* fp,MC_MathQuestion* list);
-void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length);
-
-/* these functions are dead code unless compiling with debug turned on: */
-#ifdef MC_DEBUG
-static void print_card(MC_FlashCard card);
-static void print_counters(void);
-//static MC_MathQuestion* create_node_copy(MC_MathQuestion* other);
-//static MC_FlashCard create_card_from_node(MC_MathQuestion* node);
-#endif
-
-/* Functions for new mathcards architecture */
-static void free_node(MC_MathQuestion* mq); //wrapper for free() that also frees card
-static MC_FlashCard generate_random_flashcard(void);
-static MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat);
-static void copy_card(const MC_FlashCard* src, MC_FlashCard* dest); //deep copy a flashcard
-static MC_MathQuestion* allocate_node(void); //allocate space for a node
-static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b); //test for identical cards
-static int find_divisor(int a); //return a random positive divisor of a
-static int calc_num_valid_questions(void);
-static MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list);
-static MC_MathQuestion* find_node(MC_MathQuestion* list, int num);
-
-/* MC_Initialize() sets up the struct containing all of */
-/* settings regarding math questions. It should be */
-/* called before any other function. Many of the other */
-/* functions will not work properly if MC_Initialize() */
-/* has not been called. It only needs to be called once, */
-/* i.e when the program is starting, not at the beginning*/
-/* of each math game for the player. Returns 1 if */
-/* successful, 0 otherwise. */
-int MC_Initialize(void)
-{
- int i;
-
- mcdprintf("\nEntering MC_Initialize()");
- /* check flag to see if we did this already */
- if (initialized)
- {
-
- #ifdef MC_DEBUG
- printf("\nAlready initialized");
- MC_PrintMathOptions(stdout, 0);
- printf("\nLeaving MC_Initialize()\n");
- #endif
-
- return 1;
- }
- math_opts = malloc(sizeof(MC_Options));
- /* bail out if no struct */
- if (!math_opts)
- {
- mcdprintf("\nError: malloc couldn't allocate math_opts for some reason\n");
- mcdprintf("\nLeaving MC_Initialize()\n");
-
- fprintf(stderr, "\nUnable to initialize math_options");
- return 0;
- }
-
- /* set defaults */
- for (i = 0; i < NOPTS; ++i)
- {
- math_opts->iopts[i] = MC_DEFAULTS[i];
- }
-
- /* if no negatives to be used, reset any negatives to 0 */
- if (!math_opts->iopts[ALLOW_NEGATIVES])
- {
- clear_negatives();
- }
-
- initialized = 1;
-
- #ifdef MC_DEBUG
- MC_PrintMathOptions(stdout, 0);
- printf("\nLeaving MC_Initialize()\n");
- #endif
-
- return 1;
-}
-
-
-
-/* MC_StartGame() generates the list of math questions */
-/* based on existing settings. It should be called at */
-/* the beginning of each math game for the player. */
-/* Returns 1 if resultant list contains 1 or more */
-/* questions, 0 if list empty or not generated */
-/* successfully. */
-int MC_StartGame(void)
-{
-
- mcdprintf("\nEntering MC_StartGame()");
-
- /* if math_opts not set up yet, initialize it: */
- if (!initialized)
- {
-
- mcdprintf("\nNot initialized - calling MC_Initialize()");
-
- MC_Initialize();
- }
-
- if (!math_opts)
- {
- mcdprintf("\nCould not initialize - bailing out");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 0;
- }
- /* we know math_opts exists if we make it to here */
- srand(time(NULL));
-
- /* clear out old lists if starting another game: (if not done already) */
- delete_list(question_list);
- question_list = NULL;
- delete_list(wrong_quests);
- wrong_quests = NULL;
- delete_list(active_quests);
- active_quests = NULL;
-
- /* clear the time list */
- if (time_per_question_list != NULL) {
- free(time_per_question_list);
- time_per_question_list = NULL;
- length_time_per_question_list = 0;
- length_alloc_time_per_question_list = 0;
- }
-
- //NOTE this is going away - complicates code too much to calculate this to
- //save a few bytes rather than making the string size constant
- /* determine how much space needed for strings, based on user options */
- max_formula_size = MC_GetOpt(MAX_FORMULA_NUMS)
- * (log10i(MC_GLOBAL_MAX) + 4) //sign/operator/spaces
- + 1; //question mark for answer
- max_answer_size = (int)(log10i(MC_GLOBAL_MAX) ) + 2; //negative sign + digit
-
- mcdprintf("max answer, formula size: %d, %d\n",
- max_answer_size, max_formula_size);
-
- question_list = generate_list();
-
- next_wrong_quest = 0;
- /* initialize counters for new game: */
- quest_list_length = list_length(question_list);
-
-
- /* Note: the distinction between quest_list_length and */
- /* unanswered is that the latter includes questions */
- /* that are currently "in play" by the user interface - */
- /* it is only decremented when an answer to the question*/
- /* is received. */
- unanswered = starting_length = quest_list_length;
- answered_correctly = 0;
- answered_wrong = 0;
- questions_pending = 0;
-
- #ifdef MC_DEBUG
- print_counters();
- #endif
-
- /* make sure list now exists and has non-zero length: */
- if (question_list && quest_list_length)
- {
- mcdprintf("\nGame set up successfully");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 1;
- }
- else
- {
- mcdprintf("\nGame NOT set up successfully - no valid list");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 0;
- }
-}
-
-/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
-/* but uses the incorrectly answered questions from the */
-/* previous game for the question list as a review form */
-/* of learning. If there were no wrong answers (or no */
-/* previous game), it behaves just like MC_StartGame(). */
-/* FIXME wonder if it should return a different value if */
-/* the list is created from settings because there is no */
-/* valid wrong question list? */
-int MC_StartGameUsingWrongs(void)
-{
- mcdprintf("\nEntering MC_StartGameUsingWrongs()");
-
- /* Note: if not initialized, control will pass to */
- /* MC_StartGame() via else clause so don't need to test */
- /* for initialization here */
- if (wrong_quests &&
- list_length(wrong_quests))
- {
- mcdprintf("\nNon-zero length wrong_quests list found, will");
- mcdprintf("\nuse for new game list:");
-
- /* initialize lists for new game: */
- delete_list(question_list);
- if(!randomize_list(&wrong_quests))
- {
- fprintf(stderr, "Error during randomization of wrong_quests!\n");
- /* Punt on trying wrong question list, just run normal game */
- return MC_StartGame();
- }
- question_list = wrong_quests;
- wrong_quests = 0;
- next_wrong_quest = 0;
- delete_list(active_quests);
- active_quests = 0;
- /* initialize counters for new game: */
- quest_list_length = list_length(question_list);
- unanswered = starting_length = quest_list_length;
- answered_correctly = 0;
- answered_wrong = 0;
- questions_pending = 0;
-
- #ifdef MC_DEBUG
- print_counters();
- print_list(stdout, question_list);
- printf("\nLeaving MC_StartGameUsingWrongs()\n");
- #endif
-
- return 1;
- }
- else /* if no wrong_quests list, go to MC_StartGame() */
- /* to set up list based on math_opts */
- {
- mcdprintf("\nNo wrong questions to review - generate list from math_opts\n");
- mcdprintf("\nLeaving MC_StartGameUsingWrongs()\n");
-
- return MC_StartGame();
- }
-}
-
-
-/* MC_NextQuestion() takes a pointer to an allocated */
-/* MC_MathQuestion struct and fills in the fields for */
-/* use by the user interface program. It basically is */
-/* like taking the next flashcard from the pile. The */
-/* node containing the question is removed from the list. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-/* or if argument pointer is invalid. */
-//int MC_NextQuestion(MC_FlashCard* fc)
-//{
-// mcdprintf("\nEntering MC_NextQuestion()\n");
-
- /* (so we can free the node after removed from list:) */
-// MC_MathQuestion* ptr;
-// ptr = question_list;
-
-// if (!fc )
-// {
-// fprintf(stderr, "\nNull MC_FlashCard* argument!\n");
-// mcdprintf("\nLeaving MC_NextQuestion()\n");
-// return 0;
-// }
-
-// if (!question_list ||
-/* !next_question || */
-// !list_length(question_list) )
-// {
-// mcdprintf("\nquestion_list invalid or empty");
-// mcdprintf("\nLeaving MC_NextQuestion()\n");
-
-// return 0;
-// }
-
- /* 'draw' - copy over the first question */
-// copy_card(&question_list->card, fc);
-
- /* take first question node out of list and move it into active_quests list: */
-// question_list = remove_node(question_list, question_list);
-// free_node(ptr);
-// quest_list_length--;
-// questions_pending++;
-// append_node(active_quests, ptr);
-
-// #ifdef MC_DEBUG
-// printf("\nnext question is:");
-// print_card(*fc);
-// print_counters();
-// printf("\n\nLeaving MC_NextQuestion()\n");
-// #endif
-
-// return 1;
-//}
-
-
-
-/* MC_AnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has been answered */
-/* correctly. Returns 1 if no errors. */
-int MC_AnsweredCorrectly(MC_FlashCard* fc)
-{
- mcdprintf("\nEntering MC_AnsweredCorrectly()");
-
- if (!fc)
- {
- fprintf(stderr, "\nMC_AnsweredCorrectly() passed invalid pointer as argument!\n");
-
- mcdprintf("\nInvalid MC_FlashCard* argument!");
- mcdprintf("\nLeaving MC_AnsweredCorrectly()\n");
-
- return 0;
- }
-
- #ifdef MC_DEBUG
- printf("\nQuestion was:");
- print_card(*fc);
- #endif
-
- //FIXME we need to take the question out of the active_quests list
- answered_correctly++;
- questions_pending--;
-
- if (!math_opts->iopts[PLAY_THROUGH_LIST])
- /* reinsert question into question list at random location */
- {
- mcdprintf("\nReinserting question into list");
-
- MC_MathQuestion* ptr1;
- MC_MathQuestion* ptr2;
- /* make new node using values from flashcard */
- ptr1 = create_node_from_card(fc);
- /* put it into list */
- ptr2 = pick_random(quest_list_length, question_list);
- question_list = insert_node(question_list, ptr2, ptr1);
- quest_list_length++;
- /* unanswered does not change - was not decremented when */
- /* question allocated! */
- }
- else
- {
- mcdprintf("\nNot reinserting question into list");
- /* not recycling questions so fewer questions remain: */
- unanswered--;
- }
-
- #ifdef MC_DEBUG
- print_counters();
- printf("\nLeaving MC_AnsweredCorrectly()\n");
- #endif
-
- return 1;
-}
-
-int MC_AnsweredCorrectly_id(int id)
-{
- return 1;
-}
-
-/* MC_NotAnsweredCorrectly() is how the user interface */
-/* tells MathCards that the player failed to answer the */
-/* question correctly. Returns 1 if no errors. */
-/* Note: this gets triggered only if a player's city */
-/* gets hit by a question, not if they "miss". */
-int MC_NotAnsweredCorrectly(MC_FlashCard* fc)
-{
- mcdprintf("\nEntering MC_NotAnsweredCorrectly()");
-
- if (!fc)
- {
- fprintf(stderr, "\nMC_NotAnsweredCorrectly() passed invalid pointer as argument!\n");
-
- mcdprintf("\nInvalid MC_FlashCard* argument!");
- mcdprintf("\nLeaving MC_NotAnsweredCorrectly()\n");
-
- return 0;
- }
-
- #ifdef MC_DEBUG
- printf("\nQuestion was:");
- print_card(*fc);
- #endif
-
- answered_wrong++;
- questions_pending--;
-
- /* add question to wrong_quests list: */
-
- MC_MathQuestion* ptr1;
- MC_MathQuestion* ptr2;
-
- ptr1 = create_node_from_card(fc);
-
- if (!already_in_list(wrong_quests, ptr1)) /* avoid duplicates */
- {
- mcdprintf("\nAdding to wrong_quests list");
- wrong_quests = append_node(wrong_quests, ptr1);
- }
- else /* avoid memory leak */
- {
- free(ptr1);
- }
-
- /* if desired, put question back in list so student sees it again */
- if (math_opts->iopts[REPEAT_WRONGS])
- {
- int i;
-
- mcdprintf("\nAdding %d copies to question_list:", math_opts->iopts[COPIES_REPEATED_WRONGS]);
-
- /* can put in more than one copy (to drive the point home!) */
- for (i = 0; i < math_opts->iopts[COPIES_REPEATED_WRONGS]; i++)
- {
- ptr1 = create_node_from_card(fc);
- ptr2 = pick_random(quest_list_length, question_list);
- question_list = insert_node(question_list, ptr2, ptr1);
- quest_list_length++;
- }
- /* unanswered stays the same if a single copy recycled or */
- /* increases by 1 for each "extra" copy reinserted: */
- unanswered += (math_opts->iopts[COPIES_REPEATED_WRONGS] - 1);
- }
- else
- {
- mcdprintf("\nNot repeating wrong answers\n");
-
- /* not repeating questions so list gets shorter: */
- unanswered--;
- }
-
- #ifdef MC_DEBUG
- print_counters();
- printf("\nLeaving MC_NotAnswered_Correctly()\n");
- #endif
-
- return 1;
-
-}
-
-/* Tells user interface if all questions have been answered correctly! */
-/* Requires that at list contained at least one question to start with */
-/* and that wrongly answered questions have been recycled. */
-int MC_MissionAccomplished(void)
-{
- if (starting_length
- && math_opts->iopts[REPEAT_WRONGS]
- && !unanswered)
- {
- return 1;
- }
- else
- {
- return 0;
- }
-}
-
-/* Returns number of questions left (either in list */
-/* or "in play") */
-int MC_TotalQuestionsLeft(void)
-{
- return unanswered;
-}
-
-/* Returns number of questions left in list, NOT */
-/* including questions currently "in play". */
-int MC_ListQuestionsLeft(void)
-{
- return quest_list_length;
-}
-
-
-/* Store the amount of time a given flashcard was */
-/* visible on the screen. Returns 1 if the request */
-/* succeeds, 0 otherwise. */
-int MC_AddTimeToList(float t)
-{
- int newsize = 0;
- float *newlist;
-
- /* This list will be allocated in an STL-like manner: when the */
- /* list gets full, allocate an additional amount of storage equal */
- /* to the current size of the list, so that only O(logN) allocations */
- /* will ever be needed. We therefore have to keep track of 2 sizes: */
- /* the allocated size, and the actual number of items currently on */
- /* the list. */
- if (length_time_per_question_list >= length_alloc_time_per_question_list) {
- /* The list is full, allocate more space */
- newsize = 2*length_time_per_question_list;
- if (newsize == 0)
- newsize = 100;
- newlist = realloc(time_per_question_list, newsize*sizeof(float));
- if (newlist == NULL) {
- #ifdef MC_DEBUG
- printf("\nError: allocation for time_per_question_list failed\n");
- #endif
- return 0;
- }
- time_per_question_list = newlist;
- length_alloc_time_per_question_list = newsize;
- }
-
- /* Append the time to the list */
- time_per_question_list[length_time_per_question_list++] = t;
- return 1;
-}
-
-/* Frees heap memory used in program: */
-void MC_EndGame(void)
-{
- delete_list(question_list);
- question_list = 0;
- delete_list(wrong_quests);
- wrong_quests = 0;
-
- if (math_opts)
- {
- free(math_opts);
- math_opts = 0;
- }
-
- free(time_per_question_list);
- time_per_question_list = NULL;
- length_alloc_time_per_question_list = 0;
- length_time_per_question_list = 0;
-
- initialized = 0;
-}
-
-
-
-/* prints struct to file */
-void MC_PrintMathOptions(FILE* fp, int verbose)
-{
- int i, vcommentsprimed = 0;
- //comments when writing out verbose...perhaps they can go somewhere less conspicuous
- static char* vcomments[NOPTS];
- if (!vcommentsprimed) //we only want to initialize these once
- {
- vcommentsprimed = 1;
- for (i = 0; i < NOPTS; ++i)
- vcomments[i] = NULL;
- vcomments[PLAY_THROUGH_LIST] =
- "\n############################################################\n"
- "# #\n"
- "# General Math Options #\n"
- "# #\n"
- "# If 'play_through_list' is true, Tuxmath will ask each #\n"
- "# question in an internally-generated list. The list is #\n"
- "# generated based on the question ranges selected below. #\n"
- "# The game ends when no questions remain. #\n"
- "# If 'play_through_list' is false, the game continues #\n"
- "# until all cities are destroyed. #\n"
- "# Default is 1 (i.e. 'true' or 'yes'). #\n"
- "# #\n"
- "# 'question_copies' is the number of times each question #\n"
- "# will be asked. It can be 1 to 10 - Default is 1. #\n"
- "# #\n"
- "# 'repeat_wrongs' tells Tuxmath whether to reinsert #\n"
- "# incorrectly answered questions into the list to be #\n"
- "# asked again. Default is 1 (yes). #\n"
- "# #\n"
- "# 'copies_repeated_wrongs' gives the number of times an #\n"
- "# incorrectly answered question will reappear. Default #\n"
- "# is 1. #\n"
- "# #\n"
- "# The defaults for these values result in a 'mission' #\n"
- "# for Tux that is accomplished by answering all #\n"
- "# questions correctly with at least one surviving city. #\n"
- "############################################################\n\n";
-
- vcomments[FORMAT_ADD_ANSWER_LAST] =
- "\n############################################################\n"
- "# The 'format_<op>_answer_<place> options control #\n"
- "# generation of questions with the answer in different #\n"
- "# places in the equation. i.e.: #\n"
- "# #\n"
- "# format_add_answer_last: 2 + 2 = ? #\n"
- "# format_add_answer_first: ? + 2 = 4 #\n"
- "# format_add_answer_middle: 2 + ? = 4 #\n"
- "# #\n"
- "# By default, 'format_answer_first' is enabled and the #\n"
- "# other two formats are disabled. Note that the options #\n"
- "# are not mutually exclusive - the question list may #\n"
- "# contain questions with different formats. #\n"
- "# #\n"
- "# The formats are set independently for each of the four #\n"
- "# math operations. #\n"
- "############################################################\n\n";
-
- vcomments[ALLOW_NEGATIVES] =
- "\n############################################################\n"
- "# 'allow_negatives' allows or disallows use of negative #\n"
- "# numbers as both operands and answers. Default is 0 #\n"
- "# (no), which disallows questions like: #\n"
- "# 2 - 4 = ? #\n"
- "# Note: this option must be enabled in order to set the #\n"
- "# operand ranges to include negatives (see below). If it #\n"
- "# is changed from 1 (yes) to 0 (no), any negative #\n"
- "# operand limits will be reset to 0. #\n"
- "############################################################\n\n";
-
- vcomments[MAX_ANSWER] =
- "\n############################################################\n"
- "# 'max_answer' is the largest absolute value allowed in #\n"
- "# any value in a question (not only the answer). Default #\n"
- "# is 144. It can be set as high as 999. #\n"
- "############################################################\n\n";
-
- vcomments[MAX_QUESTIONS] =
- "\n############################################################\n"
- "# 'max_questions' is limit of the length of the question #\n"
- "# list. Default is 5000 - only severe taskmasters will #\n"
- "# need to raise it. #\n"
- "############################################################\n\n";
-
- vcomments[RANDOMIZE] =
- "\n############################################################\n"
- "# If 'randomize' selected, the list will be shuffled #\n"
- "# at the start of the game. Default is 1 (yes). #\n"
- "############################################################\n\n";
-
- vcomments[ADDITION_ALLOWED] =
- "\n############################################################\n"
- "# #\n"
- "# Math Operations Allowed #\n"
- "# #\n"
- "# These options enable questions for each of the four math #\n"
- "# operations. All are 1 (yes) by default. #\n"
- "############################################################\n\n";
-
- vcomments[MIN_AUGEND] =
- "\n############################################################\n"
- "# #\n"
- "# Minimum and Maximum Values for Operand Ranges #\n"
- "# #\n"
- "# Operand limits can be set to any integer up to the #\n"
- "# value of 'max_answer'. If 'allow_negatives' is set to 1 #\n"
- "# (yes), either negative or positive values can be used. #\n"
- "# Tuxmath will generate questions for every value in the #\n"
- "# specified range. The maximum must be greater than or #\n"
- "# equal to the corresponding minimum for any questions to #\n"
- "# be generated for that operation. #\n"
- "############################################################\n\n";
-
- }
-
-
- mcdprintf("\nEntering MC_PrintMathOptions()\n");
-
- /* bail out if no struct */
- if (!math_opts)
- {
- fprintf(stderr, "\nMath Options struct does not exist!\n");
- return;
- }
-
- for (i = 0; i < NOPTS; ++i)
- {
- if (verbose && vcomments[i] != NULL)
- fprintf(fp, "%s", vcomments[i]);
- fprintf(fp, "%s = %d\n", MC_OPTION_TEXT[i], math_opts->iopts[i]);
- }
- mcdprintf("\nLeaving MC_PrintMathOptions()\n");
-}
-
-
-
-int MC_PrintQuestionList(FILE* fp)
-{
- if (fp && question_list)
- {
- print_list(fp, question_list);
- return 1;
- }
- else
- {
- fprintf(stderr, "\nFile pointer and/or question list invalid\n");
- return 0;
- }
-}
-
-int MC_PrintWrongList(FILE* fp)
-{
- if (!fp)
- {
- fprintf(stderr, "File pointer invalid\n");
- return 0;
- }
-
- if (wrong_quests)
- {
- print_list(fp, wrong_quests);
- }
- else
- {
- fprintf(fp, "\nNo wrong questions!\n");
- }
-
- return 1;
-}
-
-
-int MC_StartingListLength(void)
-{
- return starting_length;
-}
-
-
-int MC_WrongListLength(void)
-{
- return list_length(wrong_quests);
-}
-
-int MC_NumAnsweredCorrectly(void)
-{
- return answered_correctly;
-}
-
-
-int MC_NumNotAnsweredCorrectly(void)
-{
- return answered_wrong;
-}
-
-
-/* Report the median time per question */
-float MC_MedianTimePerQuestion(void)
-{
- if (length_time_per_question_list == 0)
- return 0;
-
- qsort(time_per_question_list,length_time_per_question_list,sizeof(float),floatCompare);
- return time_per_question_list[length_time_per_question_list/2];
-}
-
-/* Implementation of "private methods" - (cannot be called from outside
-of this file) */
-
-
-
-/* Resets negative values to zero - used when allow_negatives deselected. */
-void clear_negatives(void)
-{
- int i;
- for (i = MIN_AUGEND; i <= MAX_TYPING_NUM; ++i)
- if (math_opts->iopts[i]< 0)
- math_opts->iopts[i]= 0;
-}
-
-// /* this is used by generate_list to see if a possible question */
-// /* meets criteria to be added to the list or not: */
-// int validate_question(int n1, int n2, int n3)
-// {
-// /* make sure none of values exceeds max_answer using absolute */
-// /* value comparison: */
-// if (abs_value(n1) > abs_value(math_opts->iopts[MAX_ANSWER])
-// || abs_value(n2) > abs_value(math_opts->iopts[MAX_ANSWER])
-// || abs_value(n3) > abs_value(math_opts->iopts[MAX_ANSWER]))
-// {
-// return 0;
-// }
-// /* make sure none of values are negative if negatives not allowed: */
-// if (!math_opts->iopts[ALLOW_NEGATIVES])
-// {
-// if (n1 < 0 || n2 < 0 || n3 < 0)
-// {
-// return 0;
-// }
-// }
-// return 1;
-// }
-
-#if 0 //this code is probably on the way out...
-/* create a new node and return a pointer to it */
-MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f)
-{
- MC_MathQuestion* ptr = NULL;
-
- ptr = (MC_MathQuestion*)malloc(sizeof(MC_MathQuestion));
-
- if (!ptr)
- {
- fprintf(stderr, "create_node() - malloc() failed!\n");
- return NULL;
- }
-
- ptr->card = MC_AllocateFlashcard();
- ptr->next = NULL;
- ptr->previous = NULL;
-
- snprintf(ptr->card.formula_string, max_formula_size, "%d %c %d = ?",
- n1, op < MC_NUM_OPERS ? operchars[op] : '\0', n2);
- snprintf(ptr->card.answer_string, max_formula_size, "%d", ans);
- ptr->card.difficulty = 25 * (op + 1);
-
-
- /* ptr should now point to a properly constructed node: */
- return ptr;
-}
-#endif
-
-MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard)
-{
- MC_MathQuestion* ret = allocate_node();
- copy_card(flashcard, &(ret->card));
- return ret;
-}
-
-// /* FIXME take care of strings */
-// /* this one copies the contents, including pointers; both nodes must be allocated */
-// int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy)
-// {
-// if (!original)
-// {
-// fprintf(stderr, "\nIn copy_node(): invalid 'original' pointer arg.\n");
-// return 0;
-// }
-// if (!copy)
-// {
-// fprintf(stderr, "\nIn copy_node(): invalid 'copy' pointer arg.\n");
-// return 0;
-// }
-//
-// copy_card(&(original->card), &(copy->card) );
-//
-// copy->next = original->next;
-// copy->previous = original->previous;
-// copy->randomizer = original->randomizer;
-// return 1;
-// }
-
-
-
-
-/* this puts the node into the list AFTER the node pointed to by current */
-/* and returns a pointer to the top of the modified list */
-MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node)
-{
- /* return pointer to list unchanged if new_node doesn't exist*/
- if (!new_node)
- return first;
- /* if current doesn't exist, new_node is first */
- if (!current)
- {
- new_node->previous = 0;
- new_node->next =0;
- first = new_node;
- return first;
- }
-
- if (current->next) /* avoid error if at end of list */
- current->next->previous = new_node;
- new_node->next = current->next;
- current->next = new_node;
- new_node->previous = current;
- return first;
-}
-
-
-
-/* adds the new node to the end of the list */
-MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node)
-{
- MC_MathQuestion* ptr;
- /* return pointer to list unchanged if new_node doesn't exist*/
- if (!new_node)
- {
- return list;
- }
-
- /* if list does not exist, new_node is the first (and only) node */
- if (!list)
- {
- return new_node;
- }
- /* otherwise, go to end of list */
- ptr = list;
- while (ptr->next)
- {
- ptr = ptr->next;
- }
-
- ptr->next = new_node;
- new_node->previous = ptr;
- new_node->next = 0;
- return list;
-}
-
-
-
-/* this takes the node out of the list but does not delete it */
-/* and returns a pointer to the top of the modified list */
-MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n)
-{
- if (!n || !first)
- return first;
- /* special case if first node being removed */
- if (n == first)
- first = first->next;
-
- if (n->previous)
- n->previous->next = n->next;
- if (n->next)
- n->next->previous = n->previous;
- n->previous = 0;
- n->next = 0;
- return first;
-}
-
-
-
-/* frees memory for entire list and returns null pointer */
-MC_MathQuestion* delete_list(MC_MathQuestion* list)
-{
- MC_MathQuestion* tmp_ptr;
- while (list)
- {
- tmp_ptr = list->next;
- free_node (list);
- list = tmp_ptr;
- }
- return list;
-}
-
-
-
-void print_list(FILE* fp, MC_MathQuestion* list)
-{
- if (!list)
- {
- fprintf(fp, "\nprint_list(): list empty or pointer invalid\n");
- return;
- }
-
- MC_MathQuestion* ptr = list;
- while (ptr)
- {
- fprintf(stderr, "%s\n", ptr->card.formula_string);
- ptr = ptr->next;
- }
-}
-
-
-void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length)
-{
- if (!vect)
- {
- fprintf(fp, "\nprint_vect_list(): list empty or pointer invalid\n");
- return;
- }
-
- int i = 0;
- mcdprintf("Entering print_vect_list()\n");
- for(i = 0; i < length; i++)
- fprintf(fp, "%s\n", vect[i]->card.formula_string);
-
- mcdprintf("Leaving print_vect_list()\n");
-}
-
-
-
-#ifdef MC_DEBUG
-void print_card(MC_FlashCard card)
-{
- printf("\nprint_card():");
- printf("question_id=%d\nformula_string = %s\nanswer_string = %s\ndifficulty = %d\n\n",
- card.question_id,
- card.formula_string,
- card.answer_string,
- card.difficulty);
-}
-
-/* This sends the values of all "global" counters and the */
-/* lengths of the question lists to stdout - for debugging */
-void print_counters(void)
-{
- printf("\nquest_list_length = \t%d", quest_list_length);
- printf("\nlist_length(question_list) = \t%d", list_length(question_list));
- printf("\nstarting_length = \t%d", starting_length);
- printf("\nunanswered = \t%d", unanswered);
- printf("\nanswered_correctly = \t%d", answered_correctly);
- printf("\nanswered_wrong = \t%d", answered_wrong);
- printf("\nlist_length(wrong_quests) = \t%d", list_length(wrong_quests));
- printf("\nquestions_pending = \t%d", questions_pending);
-}
-
-// /* a "copy constructor", so to speak */
-// /* FIXME should properly return newly allocated list if more than one node DSB */
-// MC_MathQuestion* create_node_copy(MC_MathQuestion* other)
-// {
-// MC_MathQuestion* ret = allocate_node();
-// if (ret)
-// copy_card(&(other->card), &(ret->card) );
-// return ret;
-// }
-//
-// /* FIXME take care of strings */
-//
-// MC_FlashCard create_card_from_node(MC_MathQuestion* node)
-// {
-// MC_FlashCard fc;
-// if (!node)
-// return DEFAULT_CARD;
-// fc = MC_AllocateFlashcard();
-// copy_card(&(node->card), &fc);
-// return fc;
-// }
-#endif
-
-
-
-
-int list_length(MC_MathQuestion* list)
-{
- int length = 0;
- while (list)
- {
- length++;
- list = list->next;
- }
- return length;
-}
-
-
-
-
-
-
-/* This is a new implementation written in an attempt to avoid */
-/* the O(n^2) performance problems seen with the old randomization */
-/* function. The list is created as a vector, but is for now still */
-/* made a linked list to minimize changes needed elsewhere. */
-/* The argument is a pointer to the top of the old list. This extra */
-/* level of indirection allows the list to be shuffled "in-place". */
-/* The function returns 1 if successful, 0 on errors. */
-
-static int randomize_list(MC_MathQuestion** old_list)
-{
- MC_MathQuestion* old_tmp = *old_list;
- MC_MathQuestion** tmp_vect = NULL;
-
- int i = 0;
- if (!old_list || !*old_list) //invalid/empty list
- return 0;
-
- int old_length = list_length(old_tmp);
-
- /* set random seed: */
- srand(time(0));
-
-
- /* Allocate vector and set ptrs to nodes in old list: */
-
- /* Allocate a list of pointers, not space for the nodes themselves: */
- tmp_vect = (MC_MathQuestion**)malloc(sizeof(MC_MathQuestion*) * old_length);
- /* Set each pointer in the vector to the corresponding node: */
- for (i = 0; i < old_length; i++)
- {
- tmp_vect[i] = old_tmp;
- tmp_vect[i]->randomizer = rand();
- old_tmp = old_tmp->next;
- }
-
- /* Now simply sort on 'tmp_vect[i]->randomizer' to shuffle list: */
- qsort(tmp_vect, old_length,
- sizeof(MC_MathQuestion*),
- comp_randomizer);
-
- /* Re-create pointers to provide linked-list functionality: */
- /* (stop at 'old_length-1' because we dereference tmp_vect[i+1]) */
- for(i = 0; i < old_length - 1; i++)
- {
- if (!tmp_vect[i])
- {
- fprintf(stderr, "Invalid pointer!\n");
- return 0;
- }
- tmp_vect[i]->next = tmp_vect[i+1];
- tmp_vect[i+1]->previous = tmp_vect[i];
- }
- /* Handle end cases: */
- tmp_vect[0]->previous = NULL;
- tmp_vect[old_length-1]->next = NULL;
-
- /* Now arrange for arg pointer to indirectly point to first element! */
- *old_list = tmp_vect[0];
- free(tmp_vect);
- return 1;
-}
-
-
-
-/* This is needed for qsort(): */
-int comp_randomizer (const void* a, const void* b)
-{
-
- int int1 = (*(const struct MC_MathQuestion **) a)->randomizer;
- int int2 = (*(const struct MC_MathQuestion **) b)->randomizer;
-
- if (int1 > int2)
- return 1;
- else if (int1 == int2)
- return 0;
- else
- return -1;
-}
-
-MC_MathQuestion* pick_random(int length, MC_MathQuestion* list)
-{
- int i;
- int rand_node;
-
- /* set random seed DSB */
- srand(time(0));
-
- /* if length is zero, get out to avoid divide-by-zero error */
- if (0 == length)
- {
- return list;
- }
-
- rand_node = rand() % length;
-
- for (i=1; i < rand_node; i++)
- {
- if (list)
- list = list->next;
- }
-
- return list;
-}
-
-/* compares fields other than pointers */
-int compare_node(MC_MathQuestion* first, MC_MathQuestion* other)
-{
- if (!first || !other)
- return 0;
- if (compare_card(&(first->card), &(first->card) ) ) //cards are equal
- return 1;
- else
- return 0;
-}
-
-/* check to see if list already contains an identical node */
-int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr)
-{
- if (!list || !ptr)
- return 0;
-
- while (list)
- {
- if (compare_node(list, ptr))
- return 1;
- list = list->next;
- }
- return 0;
-}
-
-// /* to prevent option settings in math_opts from getting set to */
-// /* values other than 0 or 1 */
-// int int_to_bool(int i)
-// {
-// if (i)
-// return 1;
-// else
-// return 0;
-// }
-
-// /* prevent values from getting into math_opts that are outside */
-// /* the range that can be handled by the program (i.e. more */
-// /* than three digits; also disallow negatives if that has been */
-// /* selected. */
-// int sane_value(int i)
-// {
-// if (i > MC_GLOBAL_MAX)
-// i = MC_GLOBAL_MAX;
-// else if (i < -MC_GLOBAL_MAX)
-// i = -MC_GLOBAL_MAX;
-//
-// if (i < 0
-// && math_opts
-// && !math_opts->iopts[ALLOW_NEGATIVES])
-// {
-// i = 0;
-// }
-//
-// return i;
-// }
-
-// int abs_value(int i)
-// {
-// if (i > 0)
-// return i;
-// else
-// return -i;
-// }
-
-int log10i(int i) //base 10 logarithm for ints
-{
- int j;
- for (j = 0; i > 0; i /= 10, ++j);
- return j;
-}
-
-/* Compares two floats (needed for sorting in MC_MedianTimePerQuestion) */
-int floatCompare(const void *v1,const void *v2)
-{
- float f1,f2;
-
- f1 = *((float *) v1);
- f2 = *((float *) v2);
-
- if (f1 < f2)
- return -1;
- else if (f1 > f2)
- return 1;
- else
- return 0;
-}
-
-
-
-/****************************************************
-Functions for new mathcards architecture
-****************************************************/
-
-void copy_card(const MC_FlashCard* src, MC_FlashCard* dest)
-{
- if (!src || !dest)
- return;
- mcdprintf("Copying '%s' to '%s', ", src->formula_string,dest->formula_string);
- mcdprintf("copying '%s' to '%s'\n", src->answer_string, dest->answer_string);
- strncpy(dest->formula_string, src->formula_string, MC_FORMULA_LEN);
- strncpy(dest->answer_string, src->answer_string, MC_ANSWER_LEN);
- mcdprintf("Card is: '%s', '%s'\n", dest->formula_string, dest->answer_string);
- dest->answer = src->answer;
- dest->difficulty = src->difficulty;
- dest->question_id = src->question_id;
-}
-
-void free_node(MC_MathQuestion* mq) //no, not that freenode.
-{
- if (!mq)
- return;
- MC_FreeFlashcard(&(mq->card) );
- free(mq);
-}
-
-MC_MathQuestion* allocate_node()
-{
- MC_MathQuestion* ret = NULL;
- ret = malloc(sizeof(MC_MathQuestion) );
- if (!ret)
- {
- printf("Could not allocate space for a new node!\n");
- return NULL;
- }
-
- ret->card = MC_AllocateFlashcard();
- ret->next = ret->previous = NULL;
-
- return ret;
-}
-
-/*
-The function that does the central dirty work pertaining to flashcard
-creation. Extensible to just about any kind of math problem, perhaps
-with the exception of those with multiple answers, such as "8 + 2 > ?"
-Simply specify how the problem is presented to the user, and the
-answer the game should look for, as strings.
-*/
-MC_FlashCard generate_random_flashcard(void)
-{
- int num;
- int length;
- MC_ProblemType pt;
- MC_FlashCard ret;
- static int generate_random_flashcard_id=0;
-
- generate_random_flashcard_id+=1;
- mcdprintf("Entering generate_random_flashcard()\n");
- mcdprintf("%d\n",generate_random_flashcard_id);
- do
- pt = rand() % MC_NUM_PTYPES;
- while ( (pt == MC_PT_TYPING && !MC_GetOpt(TYPING_PRACTICE_ALLOWED) ) ||
- (pt == MC_PT_ARITHMETIC && !MC_GetOpt(ADDITION_ALLOWED) &&
- !MC_GetOpt(SUBTRACTION_ALLOWED) &&
- !MC_GetOpt(MULTIPLICATION_ALLOWED) &&
- !MC_GetOpt(DIVISION_ALLOWED) ) ||
- (pt == MC_PT_COMPARISON && !MC_GetOpt(COMPARISON_ALLOWED) )
- );
-
- if (pt == MC_PT_TYPING) //typing practice
- {
- mcdprintf("Generating typing question\n");
- ret = MC_AllocateFlashcard();
- num = rand() % (MC_GetOpt(MAX_TYPING_NUM)-MC_GetOpt(MIN_TYPING_NUM) + 1)
- + MC_GetOpt(MIN_TYPING_NUM);
- snprintf(ret.formula_string, MC_FORMULA_LEN, "%d", num);
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", num);
- ret.answer = num;
- ret.difficulty = 10;
- ret.question_id=generate_random_flashcard_id;
- }
- else //if (pt == MC_PT_ARITHMETIC)
- {
- mcdprintf("Generating arithmetic question");
- length = rand() % (MC_GetOpt(MAX_FORMULA_NUMS) -
- MC_GetOpt(MIN_FORMULA_NUMS) + 1) //avoid div by 0
- + MC_GetOpt(MIN_FORMULA_NUMS);
- mcdprintf(" of length %d", length);
- ret = generate_random_ooo_card_of_length(length, 1);
- #ifdef MC_DEBUG
- print_card(ret);
- #endif
- }
- //TODO comparison problems (e.g. "6 ? 9", "<")
-
- mcdprintf("Exiting generate_random_flashcard()\n");
-
- return ret;
-}
-
-/*
-Recursively generate an order of operations problem. Hopefully this won't
-raise performance issues. Difficulty is calculated based on the length of
-the formula and on the operators used. Problems have a 'base' difficulty of
-1 for binary operations, 3 for 3 numbers, 6, 10, etc. Each operator adds to
-the score: 0, 1, 2, and 3 respectively for addition, subtraction,
-multiplication and division.If reformat is 0, FORMAT_ANS_LAST will be used,
-otherwise a format is chosen at random.
-*/
-MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat)
-{
- int format = 0;
- int r1 = 0;
- int r2 = 0;
- int ans = 0;
- char tempstr[MC_FORMULA_LEN];
- MC_FlashCard ret;
- MC_Operation op;
- static int id = 0;
-
- id += 1;
- mcdprintf(".");
- if (length > MAX_FORMULA_NUMS)
- return DEFAULT_CARD;
- if (length <= 2)
- {
- mcdprintf("\n");
- ret = MC_AllocateFlashcard();
- for (op = rand() % MC_NUM_OPERS; //pick a random operation
- MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
- op = rand() % MC_NUM_OPERS);
-
- mcdprintf("Operation is %c\n", operchars[op]);
- /*
- if (op == MC_OPER_ADD)
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
- r2 = rand() % (math_opts->iopts[MAX_ADDEND] - math_opts->iopts[MIN_ADDEND] + 1) + math_opts->iopts[MIN_ADDEND];
- ans = r1 + r2;
- }
- else if (op == MC_OPER_SUB)
- {
- r1 = rand() % (math_opts->iopts[MAX_MINUEND] - math_opts->iopts[MIN_MINUEND] + 1) + math_opts->iopts[MIN_MINUEND];
- r2 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
- ans = r1 - r2;
- }
- else if (op == MC_OPER_MULT)
- {
- r1 = rand() % (math_opts->iopts[MAX_MULTIPLIER] - math_opts->iopts[MIN_MULTIPLIER] + 1) + math_opts->iopts[MIN_MULTIPLIER];
- r2 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_MULTIPLICAND];
- ans = r1 * r2;
- }
- else if (op == MC_OPER_DIV)
- {
- ans = rand() % (math_opts->iopts[MAX_QUOTIENT] - math_opts->iopts[MIN_QUOTIENT] + 1) + math_opts->iopts[MIN_QUOTIENT];
- r2 = rand() % (math_opts->iopts[MAX_DIVISOR] - math_opts->iopts[MIN_DIVISOR] + 1) + math_opts->iopts[MIN_DIVISOR];
- if (r2 == 0)
- r2 = 1;
- r1 = ans * r2;
- }
- */
- if (op > MC_OPER_DIV || op < MC_OPER_ADD)
- {
- mcdprintf("Invalid operator: value %d\n", op);
- return DEFAULT_CARD;
- }
- //choose two numbers in the proper range and get their result
-
- else do
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND+4*op] - math_opts->iopts[MIN_AUGEND+4*op] + 1) + math_opts->iopts[MIN_AUGEND+4*op];
- r2 = rand() % (math_opts->iopts[MAX_ADDEND+4*op] - math_opts->iopts[MIN_ADDEND+4*op] + 1) + math_opts->iopts[MIN_ADDEND+4*op];
-
- if (op == MC_OPER_ADD)
- ans = r1 + r2;
- if (op == MC_OPER_SUB)
- ans = r1 - r2;
- if (op == MC_OPER_MULT)
- ans = r1 * r2;
- if (op == MC_OPER_DIV)
- {
- if (r2 == 0)
- r2 = 1;
- ret.difficulty = r1;
- r1 *= r2;
- ans = ret.difficulty;
- }
- } while ( (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES)) || ans > MC_GetOpt(MAX_ANSWER) );
-
-
- mcdprintf("Constructing answer_string\n");
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", ans);
- mcdprintf("Constructing formula_string\n");
- snprintf(ret.formula_string, MC_FORMULA_LEN, "%d %c %d",
- r1, operchars[op], r2);
- ret.answer = ans;
- ret.difficulty = op + 1;
-
- }
- else //recurse
- {
- ret = generate_random_ooo_card_of_length(length - 1, 0);
-
- if (strchr(ret.formula_string, '+') || strchr(ret.formula_string, '-') )
- {
- //if the expression has addition or subtraction, we can't assume that
- //introducing multiplication or division will produce a predictable
- //result, so we'll limit ourselves to more addition/subtraction
- for (op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB;
- MC_GetOpt(op + ADDITION_ALLOWED) == 0;
- op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB);
-
- }
- else
- {
- //the existing expression can be treated as a number in itself, so we
- //can do anything to it and be confident of the result.
- for (op = rand() % MC_NUM_OPERS; //pick a random operation
- MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
- op = rand() % MC_NUM_OPERS);
- }
- mcdprintf("Next operation is %c,", operchars[op]);
-
- //pick the next operand
- if (op == MC_OPER_ADD)
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
- ret.answer += r1;
- }
- else if (op == MC_OPER_SUB)
- {
- r1 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
- ret.answer -= r1;
- }
- else if (op == MC_OPER_MULT)
- {
- r1 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_AUGEND];
- ret.answer *= r1;
- }
- else if (op == MC_OPER_DIV)
- {
- r1 = find_divisor(ret.answer);
- ret.answer /= r1;
- }
- else
- {
- ; //invalid operator
- }
- mcdprintf(" operand is %d\n", r1);
- mcdprintf("Answer: %d\n", ret.answer);
-
- //next append or prepend the new number (might need optimization)
- if (op == MC_OPER_SUB || op == MC_OPER_DIV || //noncommutative, append only
- rand() % 2)
- {
- snprintf(tempstr, MC_FORMULA_LEN, "%s %c %d", //append
- ret.formula_string, operchars[op], r1);
- strncpy(ret.formula_string, tempstr, MC_FORMULA_LEN);
- }
- else //we're prepending
- {
- snprintf(tempstr, MC_FORMULA_LEN, "%d %c %s", //append
- r1, operchars[op], ret.formula_string);
- strncpy(ret.formula_string, tempstr, MC_FORMULA_LEN);
- }
-
- //finally update the answer and score
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", ret.answer);
- ret.difficulty += (length - 1) + op;
- }
-
- if (reformat)
- {
- mcdprintf("Reformatting...\n");
- do {
- format = rand() % MC_NUM_FORMATS;
- } while (!MC_GetOpt(FORMAT_ANSWER_LAST + format) &&
- !MC_GetOpt(FORMAT_ADD_ANSWER_LAST + op * 3 + format) );
-
- strncat(ret.formula_string, " = ?", MC_FORMULA_LEN - strlen(ret.formula_string) );
- reformat_arithmetic(&ret, format );
- }
- ret.question_id=id;
- return ret;
-}
-
-
-
-MC_MathQuestion* generate_list(void)
-{
- int i, j;
- int length = MC_GetOpt(AVG_LIST_LENGTH);
- int cl; //raw length
- double r1, r2, delta, var; //randomizers for list length
- MC_MathQuestion* list = NULL;
- MC_MathQuestion* end_of_list = NULL;
- MC_MathQuestion* tnode = NULL;
-
-#ifdef MC_DEBUG
- MC_PrintMathOptions(stdout, 0);
-#endif
-
- if (!(MC_GetOpt(ARITHMETIC_ALLOWED) ||
- MC_GetOpt(TYPING_PRACTICE_ALLOWED) ||
- MC_GetOpt(COMPARISON_ALLOWED) ) )
- return NULL;
-
- //FIXME - remind me, why are we doing this??
- //randomize list length by a "bell curve" centered on average
- if (length && MC_GetOpt(VARY_LIST_LENGTH) )
- {
- r1 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
- r2 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
- mcdprintf("Randoms chosen: %5f, %5f\n", r1, r2);
- delta = sqrt(-2 * log(r1) ) * cos(2 * PI_VAL * r2); //standard normal dist.
- var = length / 10.0; //variance
- delta = delta * var;
- mcdprintf("Delta of average is %5f\n", delta);
- length += delta;
- if (length < 0)
- length = 1; //just in case...
- }
-
- if (MC_GetOpt(COMPREHENSIVE)) //generate all
- {
- int num_valid_questions; //How many questions the COMPREHENSIVE list specifies
- int cycles_needed; //How many times we need to generate it to get enough
-
- num_valid_questions = calc_num_valid_questions();
- if(num_valid_questions == 0)
- {
- fprintf(stderr, "generate_list() - no valid questions\n");
- return NULL;
- }
-
- cycles_needed = length/num_valid_questions;
-
- if((cycles_needed * num_valid_questions) < length)
- cycles_needed++;
-
- mcdprintf("In generate_list() - COMPREHENSIVE method requested\n");
- mcdprintf("num_valid_questions = %d\t cycles_needed = %d\n",
- num_valid_questions, cycles_needed);
-
- for (i = MC_PT_TYPING; i < MC_NUM_PTYPES; ++i)
- {
- if (!MC_GetOpt(i + TYPING_PRACTICE_ALLOWED))
- continue;
- for (j = 0; j < cycles_needed; j++)
- list = add_all_valid(i, list, &end_of_list);
- }
-
-
- if (MC_GetOpt(RANDOMIZE) )
- {
- mcdprintf("Randomizing list\n");
- randomize_list(&list);
- }
-
- if (length)
- {
- cl = list_length(list);
- // NOTE this should no longer happen - we run the COMPREHENSIVE
- // generation until we have enough questions.
- if (length > cl) //if not enough questions, pad out with randoms
- {
- mcdprintf("Padding out list from %d to %d questions\n", cl, length);
- for (i = cl; i < length; ++i)
- {
- tnode = malloc(sizeof(MC_MathQuestion) );
- if(!tnode)
- {
- fprintf(stderr, "In generate_list() - allocation failed!\n");
- delete_list(list);
- return NULL;
- }
-
- tnode->card = generate_random_flashcard();
- list = insert_node(list, end_of_list, tnode);
- end_of_list = tnode;
-// mcdprintf("%d.", list_length(list) );
- }
- }
- else if (length < cl) //if too many questions, chop off tail end of list
- {
- mcdprintf("Cutting list to %d questions\n", length);
- end_of_list = find_node(list, length);
- delete_list(end_of_list->next);
- end_of_list->next = NULL;
- }
- }
- }
-
- /* Here we are just generating random questions, one at a */
- /* time until we have enough */
- else
- {
- mcdprintf("In generate_list() - COMPREHENSIVE method NOT requested\n");
-
- for (i = 0; i < length; ++i)
- {
- tnode = malloc(sizeof(MC_MathQuestion) );
- if(!tnode)
- {
- fprintf(stderr, "In generate_list() - allocation failed!\n");
- delete_list(list);
- return NULL;
- }
-
- tnode->card = generate_random_flashcard();
- list = insert_node(list, end_of_list, tnode);
- end_of_list = tnode;
- }
- }
- return list;
-}
-
-static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b)
-{
- if (strncmp(a->formula_string, b->formula_string, MC_FORMULA_LEN) )
- return 1;
- if (strncmp(a->answer_string, b->answer_string, MC_ANSWER_LEN) )
- return 1;
- if (a->answer != b->answer);
- return 1;
-
- return 0; //the cards are identical
-}
-
-/* Public functions */
-
-/* allocate space for an MC_Flashcard */
-MC_FlashCard MC_AllocateFlashcard(void)
-{
- MC_FlashCard ret;
-
-//NOTE strings now simply hard-coded to MC_FORMULA_LEN (= 40) and
-//MC_ANSWER_LEN (= 5) instead of tailoring them to save a few bytes - DSB
-// mcdprintf("Allocating %d + %d bytes for flashcard\n",
-// max_formula_size + 1, max_answer_size + 1);
-// ret.formula_string = malloc( (max_formula_size + 1) * sizeof(char));
-// ret.answer_string = malloc( (max_answer_size + 1) * sizeof(char));
-// if (!ret.formula_string || !ret.answer_string)
-// {
-// free(ret.formula_string);
-// free(ret.answer_string);
-// printf("Couldn't allocate space for a new flashcard!\n");
-// ret = DEFAULT_CARD;
-// }
- return ret;
-}
-
-//Now a no-op - MC_FlashCard no longer has dynamically allocated strings
-void MC_FreeFlashcard(MC_FlashCard* fc)
-{
- return;
-// if (!fc)
-// return;
-// // mcdprintf("Freeing formula_string\n");
-// if (fc->formula_string)
-// {
-// free(fc->formula_string);
-// fc->formula_string = NULL;
-// }
-// // mcdprintf("Freeing answer_string\n");
-// if (fc->answer_string)
-// {
-// free(fc->answer_string);
-// fc->answer_string = NULL;
-// }
-}
-
-unsigned int MC_MapTextToIndex(const char* text)
-{
- int i;
- for (i = 0; i < NOPTS; ++i)
- {
- if (!strcasecmp(text, MC_OPTION_TEXT[i]) )
- return i;
- }
- mcdprintf("'%s' isn't a math option\n", text);
- return NOT_VALID_OPTION;
-}
-
-
-//TODO more intuitive function names for access by index vs. by text
-void MC_SetOpt(unsigned int index, int val)
-{
- if (index >= NOPTS)
- {
- mcdprintf("Invalid math option index: %d\n", index);
- return;
- }
-
- /* Do some sanity checks before we throw val into the struct: */
- switch(index)
- {
- /* All the booleans must be 0 or 1: */
- case PLAY_THROUGH_LIST:
- case REPEAT_WRONGS:
- case ALLOW_NEGATIVES:
- case FORMAT_ANSWER_LAST:
- case FORMAT_ANSWER_FIRST:
- case FORMAT_ANSWER_MIDDLE:
- case FORMAT_ADD_ANSWER_LAST:
- case FORMAT_ADD_ANSWER_FIRST:
- case FORMAT_ADD_ANSWER_MIDDLE:
- case FORMAT_SUB_ANSWER_LAST:
- case FORMAT_SUB_ANSWER_FIRST:
- case FORMAT_SUB_ANSWER_MIDDLE:
- case FORMAT_MULT_ANSWER_LAST:
- case FORMAT_MULT_ANSWER_FIRST:
- case FORMAT_MULT_ANSWER_MIDDLE:
- case FORMAT_DIV_ANSWER_LAST:
- case FORMAT_DIV_ANSWER_FIRST:
- case FORMAT_DIV_ANSWER_MIDDLE:
- case ADDITION_ALLOWED:
- case SUBTRACTION_ALLOWED:
- case MULTIPLICATION_ALLOWED:
- case DIVISION_ALLOWED:
- case TYPING_PRACTICE_ALLOWED:
- case ARITHMETIC_ALLOWED:
- case COMPARISON_ALLOWED:
- case RANDOMIZE:
- case COMPREHENSIVE:
- case VARY_LIST_LENGTH:
- {
- /* Reset all non-zero values to one: */
- if(val)
- {
- if(val != 1)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to 1\n", MC_OPTION_TEXT[index], val);
- val = 1;
- }
- }
- break;
- }
-
- /* Parameters concerning numbers of questions */
- /* must be greater than or equal to zero: */
- /* TODO some additional checks would make sense */
- case QUESTION_COPIES:
- case COPIES_REPEATED_WRONGS:
- case MAX_QUESTIONS:
- case MAX_FORMULA_NUMS:
- case MIN_FORMULA_NUMS:
- case AVG_LIST_LENGTH:
- {
- /* Reset all negative values to zero: */
- if(val < 0)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to 0\n", MC_OPTION_TEXT[index], val);
- val = 0;
- }
- break;
- }
-
- /* Operand values - make sure they are in displayable range */
- /* i.e. -999 to 999 */
- case MAX_ANSWER:
- case MIN_AUGEND:
- case MAX_AUGEND:
- case MIN_ADDEND:
- case MAX_ADDEND:
- case MIN_MINUEND:
- case MAX_MINUEND:
- case MIN_SUBTRAHEND:
- case MAX_SUBTRAHEND:
- case MIN_MULTIPLIER:
- case MAX_MULTIPLIER:
- case MIN_MULTIPLICAND:
- case MAX_MULTIPLICAND:
- case MIN_DIVISOR:
- case MAX_DIVISOR:
- case MIN_QUOTIENT:
- case MAX_QUOTIENT:
- case MIN_TYPING_NUM:
- case MAX_TYPING_NUM:
- case MIN_COMPARATOR:
- case MAX_COMPARATOR:
- case MIN_COMPARISAND:
- case MAX_COMPARISAND:
- {
- if(val > MC_GLOBAL_MAX)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to %d\n", MC_OPTION_TEXT[index],
- val, MC_GLOBAL_MAX);
- val = MC_GLOBAL_MAX;
- }
-
- if(val < (0 - MC_GLOBAL_MAX))
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to %d\n", MC_OPTION_TEXT[index],
- val, (0 - MC_GLOBAL_MAX));
- val = (0 - MC_GLOBAL_MAX);
- }
-
- break;
- }
-
- default:
- fprintf(stderr, "Warning - in MC_SetOpt() - unrecognized index %d\n",
- index);
- }
- /* Should now be safe to put "sanitized" value into struct: */
- math_opts->iopts[index] = val;
-}
-
-void MC_SetOp(const char* param, int val)
-{
- MC_SetOpt(MC_MapTextToIndex(param), val);
-}
-
-int MC_GetOpt(unsigned int index)
-{
- if (index >= NOPTS)
- {
- mcdprintf("Invalid option index: %d\n", index);
- return MC_MATH_OPTS_INVALID;
- }
- if (!math_opts)
- {
- printf("Invalid options list!\n");
- return MC_MATH_OPTS_INVALID;
- }
- return math_opts->iopts[index];
-}
-
-int MC_GetOp(const char* param)
-{
- return MC_GetOpt(MC_MapTextToIndex(param) );
-}
-
-int MC_VerifyOptionListSane(void)
-{
- return strcmp(MC_OPTION_TEXT[NOPTS], "END_OF_OPTS") == 0;
-}
-
-int MC_MaxFormulaSize(void)
-{
- return MC_FORMULA_LEN;
-}
-
-int MC_MaxAnswerSize(void)
-{
- return MC_ANSWER_LEN;
-}
-
-void MC_ResetFlashCard(MC_FlashCard* fc)
-{
- if (!fc || !fc->formula_string || !fc->answer_string)
- return;
- strncpy(fc->formula_string, " ", MC_FORMULA_LEN);
- strncpy(fc->answer_string, " ", MC_ANSWER_LEN);
- fc->answer = 0;
- fc->difficulty = 0;
- fc->question_id=0;
-}
-
-int MC_FlashCardGood(const MC_FlashCard* fc)
-{
- return fc && fc->formula_string && fc->answer_string;
-}
-
-int find_divisor(int a)
-{
- int div = 1; //the divisor to return
- int realisticpasses = 3; //reasonable time after which a minimum should be met
- int i;
- do
- for (i = 0; i < NPRIMES; ++i) //test each prime
- if (a % smallprimes[i] == 0) //if it is a prime factor,
- if (rand() % (i + 1) == 0) //maybe we'll keep it
- if (div * smallprimes[i] <= MC_GetOpt(MAX_DIVISOR) ) //if we can,
- div *= smallprimes[i]; //update our real divisor
- //keep going if the divisor is too small
- while (div < MC_GetOpt(MIN_DIVISOR) && --realisticpasses);
-
- return div;
-}
-
-
-//Computes (approximately) the number of questions that will be returned
-//by add_all_valid() as specified by the current options. This does not
-//take into account screening out of invalid questions, such
-//as divide-by-zero and questions like "0 x ? = 0".
-static int calc_num_valid_questions(void)
-{
- int total_questions = 0;
- int k = 0;
- //First add the number of typing questions
- if (MC_GetOpt(TYPING_PRACTICE_ALLOWED))
- total_questions += (MC_GetOpt(MAX_TYPING_NUM) - MC_GetOpt(MIN_TYPING_NUM));
-
- //Now add how many questions we will have for each operation:
- for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
- {
- int num_this_oper = 0;
- int formats_this_oper = 0;
-
- if (!MC_GetOpt(k + ADDITION_ALLOWED) )
- continue;
-
- //calculate number of ordered pairs of first and second operands:
- //note the "+ 1" is due to the ranges being inclusive
- num_this_oper = (MC_GetOpt(MAX_AUGEND + 4 * k) - MC_GetOpt(MIN_AUGEND + 4 * k) + 1)
- *
- (MC_GetOpt(MAX_ADDEND + 4 * k) - MC_GetOpt(MIN_ADDEND + 4 * k) + 1);
- //check what formats are allowed
- if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
- formats_this_oper++;
- if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3))
- formats_this_oper++;
- if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
- formats_this_oper++;
- //Get total of e.g. addition questions:
- num_this_oper *= formats_this_oper;
- //add to overall total:
- total_questions += num_this_oper;
- }
-
- //TODO will also need to count up the COMPARISON questions once
- //they are implemented
- {
- }
-
- mcdprintf("calc_num_valid_questions():\t%d\n", total_questions);
- return total_questions;
-}
-
-
-//NOTE end_of_list** needs to be doubly indirect because otherwise the end does not
-//get updated in the calling code
-//NOTE the difficulty is set as add = 1, sub = 2, mult = 3, div = 4, plus a 2 point
-//bonus if the format is a "missing number".
-MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list)
-{
- int i, j;
- int ans = 0, tmp;
- MC_Operation k;
- MC_MathQuestion* tnode;
-
- mcdprintf("Entering add_all_valid(%d)\n", pt);
- mcdprintf("List already has %d questions\n", list_length(list));
-
- //make sure this problem type is actually allowed
- if (!MC_GetOpt(pt + TYPING_PRACTICE_ALLOWED) )
- return list;
-
- //add all typing questions in range
- if (pt == MC_PT_TYPING)
- {
- mcdprintf("Adding typing...\n");
- for (i = MC_GetOpt(MIN_TYPING_NUM); i <= MC_GetOpt(MAX_TYPING_NUM); ++i)
- {
- mcdprintf("(%d)\n", i);
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN, "%d", i);
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", i);
- tnode->card.difficulty = 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- }
-
- //add all allowed arithmetic questions
- else if (MC_PT_ARITHMETIC)
- {
- mcdprintf("Adding arithmetic...\n");
-
- // The k loop iterates through the four arithmetic operations:
- // k = 0 means addition
- // k = 1 means subtraction
- // k = 2 means multiplication
- // k = 3 means division
- for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
- {
- if (!MC_GetOpt(k + ADDITION_ALLOWED) )
- continue;
- mcdprintf("\n*%d*\n", k);
-
- // The i loop iterates through the first value in the question:
- for (i = MC_GetOpt(MIN_AUGEND + 4 * k); i <= MC_GetOpt(MAX_AUGEND + 4 * k); ++i)
- {
- mcdprintf("\n%d:\n", i);
-
- // The j loop iterates through the second value in the question:
- for (j = MC_GetOpt(MIN_ADDEND + 4 * k); j <= MC_GetOpt(MAX_ADDEND + 4 * k); ++j)
- {
- // Generate the third number according to the operation.
- // Although it is called "ans", it will not be the actual
- // answer if it is a "missing number" type problem
- // (e.g. "3 x ? = 12")
- // We also filter out invalid questions here
- switch (k)
- {
- case MC_OPER_ADD:
- {
- ans = i + j;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_SUB:
- {
- ans = i - j;
- // throw out negatives if they aren't allowed:
- if (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES))
- continue;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_MULT:
- {
- ans = i * j;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_DIV:
- {
- // throw anything over MAX_ANSWER
- if (i * j > MC_GetOpt(MAX_ANSWER))
- continue;
-
- tmp = i;
- i *= j;
- ans = j;
- j = tmp;
- break;
- }
- default:
- fprintf(stderr, "Unrecognized operation type: %d\n", k);
- continue;
- }
-
- mcdprintf("Generating: %d %c %d = %d\n", i, operchars[k], j, ans);
-
- //add each format, provided it's allowed in general and for this op
-
- // Questions like "a + b = ?"
- if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
- {
- // Avoid division by zero:
- if (k == MC_OPER_DIV && j == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", ans);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "%d %c %d = ?", i, operchars[k], j);
- tnode->card.difficulty = k + 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
-
-
- // Questions like "? + b = c"
- if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3) )
- {
- // Avoid questions with indeterminate answer:
- // e.g. "? x 0 = 0"
- if (k == MC_OPER_MULT && j == 0)
- {
- continue;
- }
- // Avoid division by zero:
- if (k == MC_OPER_DIV && j == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", i);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "? %c %d = %d", operchars[k], j, ans);
- tnode->card.difficulty = k + 3;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
-
-
- // Questions like "a + ? = c"
- if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
- {
- // Avoid questions with indeterminate answer:
- // e.g. "0 x ? = 0"
- if (k == MC_OPER_MULT && i == 0)
- continue;
-
- // e.g. "0 / ? = 0"
- if (k == MC_OPER_DIV && i == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", j);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "%d %c ? = %d", i, operchars[k], ans);
- tnode->card.difficulty = k + 3;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- //If we divided, reset i and j so loop works correctly
- if (k == MC_OPER_DIV)
- {
- j = ans;
- i = tmp;
- mcdprintf("resetting to %d %d\n", j, i);
- }
- }
- }
- }
- }
- //add all comparison questions (TODO implement them!)
- else if (pt == MC_PT_COMPARISON)
- {
- for (i = MC_GetOpt(MIN_COMPARATOR); i < MC_GetOpt(MAX_COMPARATOR); ++i)
- {
- for (j = MC_GetOpt(MIN_COMPARISAND); j < MC_GetOpt(MAX_COMPARISAND); ++j)
- {
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN, "%d ? %d", i,j);
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN,
- i < j ? "<" :
- i > j ? ">" :
- "=");
- tnode->card.difficulty = 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- }
- }
- mcdprintf("Exiting add_all_valid()\n");
- mcdprintf("List now has %d questions\n\n", list_length(list));
-
- return list;
-}
-
-MC_MathQuestion* find_node(MC_MathQuestion* list, int num)
-{
- while (--num > 0 && list)
- list = list->next;
- return list;
-}
-
-void reformat_arithmetic(MC_FlashCard* card, MC_Format f)
-{
- int i, j;
- char* beg = 0;
- char* end = 0;
- char nans[MC_ANSWER_LEN];
- char nformula[MC_FORMULA_LEN + MC_ANSWER_LEN]; //gets a bit larger than usual in the meantime
-
- {
- //snprintf(nans, max_answer_size, "%s", card->answer_string);
-
- //insert old answer where question mark was
- for (i = 0, j = 0; card->formula_string[j] != '?'; ++i, ++j)
- nformula[i] = card->formula_string[j];
- i += snprintf(nformula + i, MC_ANSWER_LEN - 1, "%s", card->answer_string);
- snprintf(nformula + i, MC_FORMULA_LEN - i, "%s", card->formula_string + j + 1);
-
- //replace the new answer with a question mark
- if (f == MC_FORMAT_ANS_LAST)
- beg = strrchr(nformula, ' ') + 1;
- if (f == MC_FORMAT_ANS_FIRST)
- beg = nformula;
- if (f == MC_FORMAT_ANS_MIDDLE)
- beg = strchr(nformula, ' ') + 3;
- end = strchr(beg + 1, ' ');
- if (!end)
- end = "";
- //we now have beg = first digit of number to replace, end = the char after
- sscanf(beg, "%s", nans);
- *beg = 0; //sequester the first half of the string
- snprintf(card->formula_string, MC_FORMULA_LEN, "%s?%s", nformula, end);
- snprintf(card->answer_string, MC_ANSWER_LEN, "%s", nans);
- card->answer = atoi(card->answer_string);
- }
-}
Copied: tuxmath/branches/lan/src/mathcards.c (from rev 1292, tuxmath/branches/lan/server/mathcards.c)
===================================================================
--- tuxmath/branches/lan/src/mathcards.c (rev 0)
+++ tuxmath/branches/lan/src/mathcards.c 2009-07-30 02:27:03 UTC (rev 1293)
@@ -0,0 +1,2614 @@
+/*
+* C Implementation: mathcards.c
+*
+* Description: implementation of backend for a flashcard-type math game.
+ Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
+ (aka tuxmath). (If tuxmath were a C++ program, this would be a C++ class).
+ MathCards could be used as the basis for similar games using a different interface.
+
+*
+*
+* Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2005
+*
+* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
+*
+* Revised extensively in 2008 by Brendan Luchen, Tim Holy, and David Bruce
+*
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+
+#include "../src/transtruct.h"
+#include "mathcards.h"
+
+/* extern'd constants */
+
+const char* const MC_OPTION_TEXT[NOPTS+1] = {
+"PLAY_THROUGH_LIST",
+"QUESTION_COPIES",
+"REPEAT_WRONGS",
+"COPIES_REPEATED_WRONGS",
+"ALLOW_NEGATIVES",
+"MAX_ANSWER",
+"MAX_QUESTIONS",
+"MAX_FORMULA_NUMS",
+"MIN_FORMULA_NUMS",
+
+"FORMAT_ANSWER_LAST",
+"FORMAT_ANSWER_FIRST",
+"FORMAT_ANSWER_MIDDLE",
+"FORMAT_ADD_ANSWER_LAST",
+"FORMAT_ADD_ANSWER_FIRST",
+"FORMAT_ADD_ANSWER_MIDDLE",
+"FORMAT_SUB_ANSWER_LAST",
+"FORMAT_SUB_ANSWER_FIRST",
+"FORMAT_SUB_ANSWER_MIDDLE",
+"FORMAT_MULT_ANSWER_LAST",
+"FORMAT_MULT_ANSWER_FIRST",
+"FORMAT_MULT_ANSWER_MIDDLE",
+"FORMAT_DIV_ANSWER_LAST",
+"FORMAT_DIV_ANSWER_FIRST",
+"FORMAT_DIV_ANSWER_MIDDLE",
+
+"ADDITION_ALLOWED",
+"SUBTRACTION_ALLOWED",
+"MULTIPLICATION_ALLOWED",
+"DIVISION_ALLOWED",
+"TYPING_PRACTICE_ALLOWED",
+"ARITHMETIC_ALLOWED",
+"COMPARISON_ALLOWED",
+
+"MIN_AUGEND",
+"MAX_AUGEND",
+"MIN_ADDEND",
+"MAX_ADDEND",
+
+"MIN_MINUEND",
+"MAX_MINUEND",
+"MIN_SUBTRAHEND",
+"MAX_SUBTRAHEND",
+
+"MIN_MULTIPLIER",
+"MAX_MULTIPLIER",
+"MIN_MULTIPLICAND",
+"MAX_MULTIPLICAND",
+
+"MIN_DIVISOR",
+"MAX_DIVISOR",
+"MIN_QUOTIENT",
+"MAX_QUOTIENT",
+
+"MIN_TYPING_NUM",
+"MAX_TYPING_NUM",
+
+"MIN_COMPARATOR" ,
+"MAX_COMPARATOR" ,
+"MIN_COMPARISAND",
+"MAX_COMPARISAND",
+
+"RANDOMIZE",
+
+"COMPREHENSIVE",
+"AVG_LIST_LENGTH",
+"VARY_LIST_LENGTH",
+
+"END_OF_OPTS"
+};
+
+
+
+const int MC_DEFAULTS[] = {
+ 1, //PLAY_THROUGH_LIST
+ 1, //QUESTION_COPIES
+ 1, //REPEAT_WRONGS
+ 1, //COPIES_REPEATED_WRONGS
+ 0, //ALLOW_NEGATIVES
+ 999, //MAX_ANSWER
+ 5000, //MAX_QUESTIONS
+ 2, //MAX_FORMULA_NUMS
+ 2, //MIN_FORMULA_NUMS
+ //
+ 1, //FORMAT_ANSWER_LAST
+ 0, //FORMAT_ANSWER_FIRST
+ 0, //FORMAT_ANSWER_MIDDLE
+ 1, //FORMAT_ADD_ANSWER_LAST
+ 0, //FORMAT_ADD_ANSWER_FIRST
+ 0, //FORMAT_ADD_ANSWER_MIDDLE
+ 1, //FORMAT_SUB_ANSWER_LAST
+ 0, //FORMAT_SUB_ANSWER_FIRST
+ 0, //FORMAT_SUB_ANSWER_MIDDLE
+ 1, //FORMAT_MULT_ANSWER_LAST
+ 0, //FORMAT_MULT_ANSWER_FIRST
+ 0, //FORMAT_MULT_ANSWER_MIDDLE
+ 1, //FORMAT_DIV_ANSWER_LAST
+ 0, //FORMAT_DIV_ANSWER_FIRST
+ 0, //FORMAT_DIV_ANSWER_MIDDLE
+ //
+ 1, //ADDITION_ALLOWED
+ 1, //SUBTRACTION_ALLOWED
+ 1, //MULTIPLICATION_ALLOWED
+ 1, //DIVISION_ALLOWED
+
+ 0, //TYPING_PRACTICE_ALLOWED
+ 1, //ARITHMETIC_ALLOWED
+ 0, //COMPARISON_ALLOWED
+ //
+ 0, //MIN_AUGEND
+ 12, //MAX_AUGEND
+ 0, //MIN_ADDEND
+ 12, //MAX_ADDEND
+ //
+ 0, //MIN_MINUEND
+ 12, //MAX_MINUEND
+ 0, //MIN_SUBTRAHEND
+ 12, //MAX_SUBTRAHEND
+ //
+ 0, //MIN_MULTIPLIER
+ 12, //MAX_MULTIPLIER
+ 0, //MIN_MULTIPLICAND
+ 12, //MAX_MULTIPLICAND
+ //
+ 0, //MIN_DIVISOR
+ 12, //MAX_DIVISOR
+ 0, //MIN_QUOTIENT
+ 12, //MAX_QUOTIENT
+ //
+ 0, //MIN_TYPING_NUM
+ 12, //MAX_TYPING_NUM
+ //
+ 0, //MIN_COMPARATOR
+ 12, //MAX_COMPARATOR
+ 0, //MIN_COMPARISAND
+ 12, //MAX_COMPARISAND
+
+ 1, //RANDOMIZE
+
+ 0, //COMPREHENSIVE
+ 100, //AVG_LIST_LENGTH
+ 0 //VARY_LIST_LENGTH FIXME what is the purpose of this?
+};
+
+
+
+/* "Globals" for mathcards.c: */
+#define PI_VAL 3.1415927
+#define NPRIMES 9
+const int smallprimes[NPRIMES] = {2, 3, 5 ,7, 11, 13, 17, 19, 23};
+const char operchars[4] = "+-*/";
+extern int n;
+
+MC_Options* math_opts = 0;
+MC_MathQuestion* question_list = 0;
+MC_MathQuestion* wrong_quests = 0;
+MC_MathQuestion* active_quests = 0;
+MC_MathQuestion* next_wrong_quest = 0;
+int initialized = 0;
+int quest_list_length = 0;
+int answered_correctly = 0;
+int answered_wrong = 0;
+int questions_pending = 0;
+int unanswered = 0;
+int starting_length = 0;
+//NOTE these are no longer used:
+int max_formula_size = 0; //max length in chars of a flashcard's formula
+int max_answer_size = 0; //and of its answer
+
+/* For keeping track of timing data */
+/*FIXME do we really need any of these? */
+float* time_per_question_list = NULL;
+int length_time_per_question_list = 0;
+int length_alloc_time_per_question_list = 0;
+
+const MC_FlashCard DEFAULT_CARD = {0,0,0,0}; //empty card to signal error
+
+/* "private" function prototypes: */
+/* */
+/* these are for internal use by MathCards only - like */
+/* the private functions of a C++ class. Declared static */
+/* to give file scope rather than extern scope. */
+
+static MC_MathQuestion* generate_list(void);
+static void clear_negatives(void);
+//static int validate_question(int n1, int n2, int n3);
+//static MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f);
+static MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard);
+static MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node);
+static MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node);
+static MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n);
+static MC_MathQuestion* delete_list(MC_MathQuestion* list);
+//static int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy);
+static int list_length(MC_MathQuestion* list);
+static int randomize_list(MC_MathQuestion** list);
+
+int comp_randomizer(const void *a, const void *b);
+static MC_MathQuestion* pick_random(int length, MC_MathQuestion* list);
+static int compare_node(MC_MathQuestion* first, MC_MathQuestion* other);
+static int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr);
+//static int int_to_bool(int i);
+//static int sane_value(int i);
+//static int abs_value(int i);
+static int log10i(int i);
+static int floatCompare(const void *v1,const void *v2);
+
+static void print_list(FILE* fp,MC_MathQuestion* list);
+void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length);
+
+/* these functions are dead code unless compiling with debug turned on: */
+#ifdef MC_DEBUG
+static void print_card(MC_FlashCard card);
+static void print_counters(void);
+//static MC_MathQuestion* create_node_copy(MC_MathQuestion* other);
+//static MC_FlashCard create_card_from_node(MC_MathQuestion* node);
+#endif
+
+/* Functions for new mathcards architecture */
+static void free_node(MC_MathQuestion* mq); //wrapper for free() that also frees card
+static MC_FlashCard generate_random_flashcard(void);
+static MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat);
+static void copy_card(const MC_FlashCard* src, MC_FlashCard* dest); //deep copy a flashcard
+static MC_MathQuestion* allocate_node(void); //allocate space for a node
+static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b); //test for identical cards
+static int find_divisor(int a); //return a random positive divisor of a
+static int calc_num_valid_questions(void);
+static MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list);
+static MC_MathQuestion* find_node(MC_MathQuestion* list, int num);
+
+/* MC_Initialize() sets up the struct containing all of */
+/* settings regarding math questions. It should be */
+/* called before any other function. Many of the other */
+/* functions will not work properly if MC_Initialize() */
+/* has not been called. It only needs to be called once, */
+/* i.e when the program is starting, not at the beginning*/
+/* of each math game for the player. Returns 1 if */
+/* successful, 0 otherwise. */
+int MC_Initialize(void)
+{
+ int i;
+
+ mcdprintf("\nEntering MC_Initialize()");
+ /* check flag to see if we did this already */
+ if (initialized)
+ {
+
+ #ifdef MC_DEBUG
+ printf("\nAlready initialized");
+ MC_PrintMathOptions(stdout, 0);
+ printf("\nLeaving MC_Initialize()\n");
+ #endif
+
+ return 1;
+ }
+ math_opts = malloc(sizeof(MC_Options));
+ /* bail out if no struct */
+ if (!math_opts)
+ {
+ mcdprintf("\nError: malloc couldn't allocate math_opts for some reason\n");
+ mcdprintf("\nLeaving MC_Initialize()\n");
+
+ fprintf(stderr, "\nUnable to initialize math_options");
+ return 0;
+ }
+
+ /* set defaults */
+ for (i = 0; i < NOPTS; ++i)
+ {
+ math_opts->iopts[i] = MC_DEFAULTS[i];
+ }
+
+ /* if no negatives to be used, reset any negatives to 0 */
+ if (!math_opts->iopts[ALLOW_NEGATIVES])
+ {
+ clear_negatives();
+ }
+
+ initialized = 1;
+
+ #ifdef MC_DEBUG
+ MC_PrintMathOptions(stdout, 0);
+ printf("\nLeaving MC_Initialize()\n");
+ #endif
+
+ return 1;
+}
+
+
+
+/* MC_StartGame() generates the list of math questions */
+/* based on existing settings. It should be called at */
+/* the beginning of each math game for the player. */
+/* Returns 1 if resultant list contains 1 or more */
+/* questions, 0 if list empty or not generated */
+/* successfully. */
+int MC_StartGame(void)
+{
+
+ mcdprintf("\nEntering MC_StartGame()");
+
+ /* if math_opts not set up yet, initialize it: */
+ if (!initialized)
+ {
+
+ mcdprintf("\nNot initialized - calling MC_Initialize()");
+
+ MC_Initialize();
+ }
+
+ if (!math_opts)
+ {
+ mcdprintf("\nCould not initialize - bailing out");
+ mcdprintf("\nLeaving MC_StartGame()\n");
+
+ return 0;
+ }
+ /* we know math_opts exists if we make it to here */
+ srand(time(NULL));
+
+ /* clear out old lists if starting another game: (if not done already) */
+ delete_list(question_list);
+ question_list = NULL;
+ delete_list(wrong_quests);
+ wrong_quests = NULL;
+ delete_list(active_quests);
+ active_quests = NULL;
+
+ /* clear the time list */
+ if (time_per_question_list != NULL) {
+ free(time_per_question_list);
+ time_per_question_list = NULL;
+ length_time_per_question_list = 0;
+ length_alloc_time_per_question_list = 0;
+ }
+
+ //NOTE this is going away - complicates code too much to calculate this to
+ //save a few bytes rather than making the string size constant
+ /* determine how much space needed for strings, based on user options */
+ max_formula_size = MC_GetOpt(MAX_FORMULA_NUMS)
+ * (log10i(MC_GLOBAL_MAX) + 4) //sign/operator/spaces
+ + 1; //question mark for answer
+ max_answer_size = (int)(log10i(MC_GLOBAL_MAX) ) + 2; //negative sign + digit
+
+ mcdprintf("max answer, formula size: %d, %d\n",
+ max_answer_size, max_formula_size);
+
+ question_list = generate_list();
+
+ next_wrong_quest = 0;
+ /* initialize counters for new game: */
+ quest_list_length = list_length(question_list);
+
+
+ /* Note: the distinction between quest_list_length and */
+ /* unanswered is that the latter includes questions */
+ /* that are currently "in play" by the user interface - */
+ /* it is only decremented when an answer to the question*/
+ /* is received. */
+ unanswered = starting_length = quest_list_length;
+ answered_correctly = 0;
+ answered_wrong = 0;
+ questions_pending = 0;
+
+ #ifdef MC_DEBUG
+ print_counters();
+ #endif
+
+ /* make sure list now exists and has non-zero length: */
+ if (question_list && quest_list_length)
+ {
+ mcdprintf("\nGame set up successfully");
+ mcdprintf("\nLeaving MC_StartGame()\n");
+
+ return 1;
+ }
+ else
+ {
+ mcdprintf("\nGame NOT set up successfully - no valid list");
+ mcdprintf("\nLeaving MC_StartGame()\n");
+
+ return 0;
+ }
+}
+
+/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
+/* but uses the incorrectly answered questions from the */
+/* previous game for the question list as a review form */
+/* of learning. If there were no wrong answers (or no */
+/* previous game), it behaves just like MC_StartGame(). */
+/* FIXME wonder if it should return a different value if */
+/* the list is created from settings because there is no */
+/* valid wrong question list? */
+int MC_StartGameUsingWrongs(void)
+{
+ mcdprintf("\nEntering MC_StartGameUsingWrongs()");
+
+ /* Note: if not initialized, control will pass to */
+ /* MC_StartGame() via else clause so don't need to test */
+ /* for initialization here */
+ if (wrong_quests &&
+ list_length(wrong_quests))
+ {
+ mcdprintf("\nNon-zero length wrong_quests list found, will");
+ mcdprintf("\nuse for new game list:");
+
+ /* initialize lists for new game: */
+ delete_list(question_list);
+ if(!randomize_list(&wrong_quests))
+ {
+ fprintf(stderr, "Error during randomization of wrong_quests!\n");
+ /* Punt on trying wrong question list, just run normal game */
+ return MC_StartGame();
+ }
+ question_list = wrong_quests;
+ wrong_quests = 0;
+ next_wrong_quest = 0;
+ delete_list(active_quests);
+ active_quests = 0;
+ /* initialize counters for new game: */
+ quest_list_length = list_length(question_list);
+ unanswered = starting_length = quest_list_length;
+ answered_correctly = 0;
+ answered_wrong = 0;
+ questions_pending = 0;
+
+ #ifdef MC_DEBUG
+ print_counters();
+ print_list(stdout, question_list);
+ printf("\nLeaving MC_StartGameUsingWrongs()\n");
+ #endif
+
+ return 1;
+ }
+ else /* if no wrong_quests list, go to MC_StartGame() */
+ /* to set up list based on math_opts */
+ {
+ mcdprintf("\nNo wrong questions to review - generate list from math_opts\n");
+ mcdprintf("\nLeaving MC_StartGameUsingWrongs()\n");
+
+ return MC_StartGame();
+ }
+}
+
+
+/* MC_NextQuestion() takes a pointer to an allocated */
+/* MC_MathQuestion struct and fills in the fields for */
+/* use by the user interface program. It basically is */
+/* like taking the next flashcard from the pile. The */
+/* node containing the question is removed from the list. */
+/* Returns 1 if question found, 0 if list empty/invalid */
+/* or if argument pointer is invalid. */
+int MC_NextQuestion(MC_FlashCard* fc)
+{
+ mcdprintf("\nEntering MC_NextQuestion()\n");
+
+ /* (so we can move the node into active_quests:) */
+ MC_MathQuestion* ptr;
+
+ if (!fc )
+ {
+ fprintf(stderr, "\nNull MC_FlashCard* argument!\n");
+ mcdprintf("\nLeaving MC_NextQuestion()\n");
+ return 0;
+ }
+
+ if (!question_list ||
+/* !next_question || */
+ !list_length(question_list) )
+ {
+ mcdprintf("\nquestion_list invalid or empty");
+ mcdprintf("\nLeaving MC_NextQuestion()\n");
+
+ return 0;
+ }
+
+ /* 'draw' - copy over the first question */
+ copy_card(&question_list->card, fc);
+
+ /* take first question node out of list and move it into active_quests list: */
+ ptr = question_list;
+ question_list = remove_node(question_list, ptr);
+// free_node(ptr);
+ quest_list_length--;
+ questions_pending++;
+ active_quests = append_node(active_quests, ptr);
+
+ #ifdef MC_DEBUG
+ printf("\nnext question is:");
+ print_card(*fc);
+ print_counters();
+ printf("\n\nLeaving MC_NextQuestion()\n");
+ #endif
+
+ return 1;
+}
+
+
+
+/* MC_AnsweredCorrectly() is how the user interface */
+/* tells MathCards that the question has been answered */
+/* correctly. Returns 1 if no errors. */
+int MC_AnsweredCorrectly(MC_FlashCard* fc)
+{
+ mcdprintf("\nEntering MC_AnsweredCorrectly()");
+
+ MC_MathQuestion* quest = NULL;
+
+ if (!fc)
+ {
+ fprintf(stderr, "\nMC_AnsweredCorrectly() passed invalid pointer as argument!\n");
+
+ mcdprintf("\nInvalid MC_FlashCard* argument!");
+ mcdprintf("\nLeaving MC_AnsweredCorrectly()\n");
+
+ return 0;
+ }
+
+ if(!active_quests) // No questions currently "in play" - something is wrong:
+ {
+ fprintf(stderr, "MC_AnsweredCorrectly() - active_quests empty\n");
+ return 0;
+ }
+
+ #ifdef MC_DEBUG
+ printf("\nQuestion was:");
+ print_card(*fc);
+ #endif
+
+
+ //First take the question out of the active_quests list
+ quest = active_quests;
+ // Loop until quest is NULL or we find card with same id:
+ while(quest && (fc->question_id != quest->card.question_id))
+ quest = quest->next;
+ if(!quest) // Means we didn't find matching card - something is wrong:
+ {
+ fprintf(stderr, "MC_AnsweredCorrectly() - matching question not found!\n");
+ return 0;
+ }
+ #ifdef MC_DEBUG
+ printf("\nMatching question is:");
+ print_card(quest->card);
+ #endif
+
+ //We found a matching question, now we take it out of the
+ //"active_quests" list and either put it back into the
+ //main question list in a random location, or delete it:
+ active_quests = remove_node(active_quests, quest);
+ questions_pending--; //the length of the 'active_quests' list
+
+
+ answered_correctly++;
+
+ if (!math_opts->iopts[PLAY_THROUGH_LIST])
+ /* reinsert question into question list at random location */
+ {
+ mcdprintf("\nReinserting question into list");
+
+ MC_MathQuestion* rand_spot;
+ /* put it into list */
+ rand_spot = pick_random(quest_list_length, question_list);
+ question_list = insert_node(question_list, rand_spot, quest);
+ quest_list_length++;
+ /* unanswered does not change - was not decremented when */
+ /* question allocated! */
+ }
+ else
+ {
+ mcdprintf("\nNot reinserting question into list");
+ free_node(quest);
+ /* not recycling questions so fewer questions remain: */
+ unanswered--;
+ }
+
+ #ifdef MC_DEBUG
+ print_counters();
+ printf("\nLeaving MC_AnsweredCorrectly()\n");
+ #endif
+
+ return 1;
+}
+
+
+
+int MC_AnsweredCorrectly_id(int id)
+{
+ MC_MathQuestion* mq;
+ MC_FlashCard* fc;
+
+ if(!active_quests)
+ {
+ mcdprintf("MC_AnsweredCorrectly_id() - active_quests is empty\n");
+ return 0;
+ }
+ //Find the question with the given id, if it exists:
+ //First take the question out of the active_quests list
+ mq = active_quests;
+ // Loop until mq is NULL or card found with matching id:
+ while(mq && (id != mq->card.question_id))
+ {
+ mcdprintf("id is %d, mq->card.question_id is %d\n", id, mq->card.question_id);
+ mq = mq->next;
+ }
+ if(!mq) // Means we didn't find matching card - something is wrong:
+ {
+ fprintf(stderr, "MC_AnsweredCorrectly_id() - matching question not found for id = %d!\n", id);
+ return 0;
+ }
+ //Now just pass address of card field to MC_AnsweredCorrectly():
+ fc = &(mq->card);
+ return MC_AnsweredCorrectly(fc);
+}
+
+
+
+/* MC_NotAnsweredCorrectly() is how the user interface */
+/* tells MathCards that the player failed to answer the */
+/* question correctly. Returns 1 if no errors. */
+/* Note: this gets triggered only if a player's igloo/city */
+/* gets hit by a question, not if they "miss". */
+int MC_NotAnsweredCorrectly(MC_FlashCard* fc)
+{
+ mcdprintf("\nEntering MC_NotAnsweredCorrectly()");
+
+ MC_MathQuestion* quest = NULL;
+
+ if (!fc)
+ {
+ fprintf(stderr, "\nMC_NotAnsweredCorrectly() passed invalid pointer as argument!\n");
+
+ mcdprintf("\nInvalid MC_FlashCard* argument!");
+ mcdprintf("\nLeaving MC_NotAnsweredCorrectly()\n");
+
+ return 0;
+ }
+
+ if(!active_quests) // No questions currently "in play" - something is wrong:
+ {
+ fprintf(stderr, "MC_NotAnsweredCorrectly() - active_quests empty\n");
+ return 0;
+ }
+
+ #ifdef MC_DEBUG
+ printf("\nQuestion was:");
+ print_card(*fc);
+ #endif
+
+
+ //First take the question out of the active_quests list
+ quest = active_quests;
+ // Loop until quest is NULL or we find card with same id:
+ while(quest && (fc->question_id != quest->card.question_id))
+ quest = quest->next;
+ if(!quest) // Means we didn't find matching card - something is wrong:
+ {
+ fprintf(stderr, "MC_NotAnsweredCorrectly() - matching question not found!\n");
+ return 0;
+ }
+ #ifdef MC_DEBUG
+ printf("\nMatching question is:");
+ print_card(quest->card);
+ #endif
+
+ //We found a matching question, now we take it out of the
+ //"active_quests" list and either put it back into the
+ //main question list in a random location, or delete it:
+ active_quests = remove_node(active_quests, quest);
+ questions_pending--; //the length of the 'active_quests' list
+
+ answered_wrong++;
+
+ /* add question to wrong_quests list: */
+ if (!already_in_list(wrong_quests, quest)) /* avoid duplicates */
+ {
+ mcdprintf("\nAdding to wrong_quests list");
+ wrong_quests = append_node(wrong_quests, quest);
+ }
+ else /* avoid memory leak */
+ {
+ free(quest);
+ }
+
+ /* if desired, put question back in list so student sees it again */
+ if (math_opts->iopts[REPEAT_WRONGS])
+ {
+ int i;
+ MC_MathQuestion* quest_copy;
+ MC_MathQuestion* rand_loc;
+
+ mcdprintf("\nAdding %d copies to question_list:", math_opts->iopts[COPIES_REPEATED_WRONGS]);
+
+ /* can put in more than one copy (to drive the point home!) */
+ for (i = 0; i < math_opts->iopts[COPIES_REPEATED_WRONGS]; i++)
+ {
+ quest_copy = create_node_from_card(fc);
+ rand_loc = pick_random(quest_list_length, question_list);
+ question_list = insert_node(question_list, rand_loc, quest_copy);
+ quest_list_length++;
+ }
+ /* unanswered stays the same if a single copy recycled or */
+ /* increases by 1 for each "extra" copy reinserted: */
+ unanswered += (math_opts->iopts[COPIES_REPEATED_WRONGS] - 1);
+ }
+ else
+ {
+ mcdprintf("\nNot repeating wrong answers\n");
+
+ /* not repeating questions so list gets shorter: */
+ unanswered--;
+ }
+
+ #ifdef MC_DEBUG
+ print_counters();
+ printf("\nLeaving MC_NotAnswered_Correctly()\n");
+ #endif
+
+ return 1;
+
+}
+
+
+int MC_NotAnsweredCorrectly_id(int id)
+{
+ MC_MathQuestion* mq;
+ MC_FlashCard* fc;
+
+ if(!active_quests)
+ {
+ mcdprintf("MC_NotAnsweredCorrectly_id() - active_quests is empty\n");
+ return 0;
+ }
+ //Find the question with the given id, if it exists:
+ //First take the question out of the active_quests list
+ mq = active_quests;
+ // Loop until mq is NULL or card found with matching id:
+ while(mq && (id != mq->card.question_id))
+ {
+ mcdprintf("id is %d, mq->card.question_id is %d\n", id, mq->card.question_id);
+ mq = mq->next;
+ }
+ if(!mq) // Means we didn't find matching card - something is wrong:
+ {
+ fprintf(stderr, "MC_NotAnsweredCorrectly_id() - matching question not found!\n");
+ return 0;
+ }
+ //Now just pass address of card field to MC_NotAnsweredCorrectly():
+ fc = &(mq->card);
+ return MC_NotAnsweredCorrectly(fc);
+}
+
+
+
+/* Tells user interface if all questions have been answered correctly! */
+/* Requires that at list contained at least one question to start with */
+/* and that wrongly answered questions have been recycled. */
+int MC_MissionAccomplished(void)
+{
+ if (starting_length
+ && math_opts->iopts[REPEAT_WRONGS]
+ && !unanswered)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/* Returns number of questions left (either in list */
+/* or "in play") */
+int MC_TotalQuestionsLeft(void)
+{
+ return unanswered;
+}
+
+/* Returns number of questions left in list, NOT */
+/* including questions currently "in play". */
+int MC_ListQuestionsLeft(void)
+{
+ return quest_list_length;
+}
+
+
+/* Store the amount of time a given flashcard was */
+/* visible on the screen. Returns 1 if the request */
+/* succeeds, 0 otherwise. */
+int MC_AddTimeToList(float t)
+{
+ int newsize = 0;
+ float *newlist;
+
+ /* This list will be allocated in an STL-like manner: when the */
+ /* list gets full, allocate an additional amount of storage equal */
+ /* to the current size of the list, so that only O(logN) allocations */
+ /* will ever be needed. We therefore have to keep track of 2 sizes: */
+ /* the allocated size, and the actual number of items currently on */
+ /* the list. */
+ if (length_time_per_question_list >= length_alloc_time_per_question_list) {
+ /* The list is full, allocate more space */
+ newsize = 2*length_time_per_question_list;
+ if (newsize == 0)
+ newsize = 100;
+ newlist = realloc(time_per_question_list, newsize*sizeof(float));
+ if (newlist == NULL) {
+ #ifdef MC_DEBUG
+ printf("\nError: allocation for time_per_question_list failed\n");
+ #endif
+ return 0;
+ }
+ time_per_question_list = newlist;
+ length_alloc_time_per_question_list = newsize;
+ }
+
+ /* Append the time to the list */
+ time_per_question_list[length_time_per_question_list++] = t;
+ return 1;
+}
+
+/* Frees heap memory used in program: */
+void MC_EndGame(void)
+{
+ delete_list(question_list);
+ question_list = 0;
+ delete_list(wrong_quests);
+ wrong_quests = 0;
+
+ if (math_opts)
+ {
+ free(math_opts);
+ math_opts = 0;
+ }
+
+ free(time_per_question_list);
+ time_per_question_list = NULL;
+ length_alloc_time_per_question_list = 0;
+ length_time_per_question_list = 0;
+
+ initialized = 0;
+}
+
+
+
+/* prints struct to file */
+void MC_PrintMathOptions(FILE* fp, int verbose)
+{
+ int i, vcommentsprimed = 0;
+ //comments when writing out verbose...perhaps they can go somewhere less conspicuous
+ static char* vcomments[NOPTS];
+ if (!vcommentsprimed) //we only want to initialize these once
+ {
+ vcommentsprimed = 1;
+ for (i = 0; i < NOPTS; ++i)
+ vcomments[i] = NULL;
+ vcomments[PLAY_THROUGH_LIST] =
+ "\n############################################################\n"
+ "# #\n"
+ "# General Math Options #\n"
+ "# #\n"
+ "# If 'play_through_list' is true, Tuxmath will ask each #\n"
+ "# question in an internally-generated list. The list is #\n"
+ "# generated based on the question ranges selected below. #\n"
+ "# The game ends when no questions remain. #\n"
+ "# If 'play_through_list' is false, the game continues #\n"
+ "# until all cities are destroyed. #\n"
+ "# Default is 1 (i.e. 'true' or 'yes'). #\n"
+ "# #\n"
+ "# 'question_copies' is the number of times each question #\n"
+ "# will be asked. It can be 1 to 10 - Default is 1. #\n"
+ "# #\n"
+ "# 'repeat_wrongs' tells Tuxmath whether to reinsert #\n"
+ "# incorrectly answered questions into the list to be #\n"
+ "# asked again. Default is 1 (yes). #\n"
+ "# #\n"
+ "# 'copies_repeated_wrongs' gives the number of times an #\n"
+ "# incorrectly answered question will reappear. Default #\n"
+ "# is 1. #\n"
+ "# #\n"
+ "# The defaults for these values result in a 'mission' #\n"
+ "# for Tux that is accomplished by answering all #\n"
+ "# questions correctly with at least one surviving city. #\n"
+ "############################################################\n\n";
+
+ vcomments[FORMAT_ADD_ANSWER_LAST] =
+ "\n############################################################\n"
+ "# The 'format_<op>_answer_<place> options control #\n"
+ "# generation of questions with the answer in different #\n"
+ "# places in the equation. i.e.: #\n"
+ "# #\n"
+ "# format_add_answer_last: 2 + 2 = ? #\n"
+ "# format_add_answer_first: ? + 2 = 4 #\n"
+ "# format_add_answer_middle: 2 + ? = 4 #\n"
+ "# #\n"
+ "# By default, 'format_answer_first' is enabled and the #\n"
+ "# other two formats are disabled. Note that the options #\n"
+ "# are not mutually exclusive - the question list may #\n"
+ "# contain questions with different formats. #\n"
+ "# #\n"
+ "# The formats are set independently for each of the four #\n"
+ "# math operations. #\n"
+ "############################################################\n\n";
+
+ vcomments[ALLOW_NEGATIVES] =
+ "\n############################################################\n"
+ "# 'allow_negatives' allows or disallows use of negative #\n"
+ "# numbers as both operands and answers. Default is 0 #\n"
+ "# (no), which disallows questions like: #\n"
+ "# 2 - 4 = ? #\n"
+ "# Note: this option must be enabled in order to set the #\n"
+ "# operand ranges to include negatives (see below). If it #\n"
+ "# is changed from 1 (yes) to 0 (no), any negative #\n"
+ "# operand limits will be reset to 0. #\n"
+ "############################################################\n\n";
+
+ vcomments[MAX_ANSWER] =
+ "\n############################################################\n"
+ "# 'max_answer' is the largest absolute value allowed in #\n"
+ "# any value in a question (not only the answer). Default #\n"
+ "# is 144. It can be set as high as 999. #\n"
+ "############################################################\n\n";
+
+ vcomments[MAX_QUESTIONS] =
+ "\n############################################################\n"
+ "# 'max_questions' is limit of the length of the question #\n"
+ "# list. Default is 5000 - only severe taskmasters will #\n"
+ "# need to raise it. #\n"
+ "############################################################\n\n";
+
+ vcomments[RANDOMIZE] =
+ "\n############################################################\n"
+ "# If 'randomize' selected, the list will be shuffled #\n"
+ "# at the start of the game. Default is 1 (yes). #\n"
+ "############################################################\n\n";
+
+ vcomments[ADDITION_ALLOWED] =
+ "\n############################################################\n"
+ "# #\n"
+ "# Math Operations Allowed #\n"
+ "# #\n"
+ "# These options enable questions for each of the four math #\n"
+ "# operations. All are 1 (yes) by default. #\n"
+ "############################################################\n\n";
+
+ vcomments[MIN_AUGEND] =
+ "\n############################################################\n"
+ "# #\n"
+ "# Minimum and Maximum Values for Operand Ranges #\n"
+ "# #\n"
+ "# Operand limits can be set to any integer up to the #\n"
+ "# value of 'max_answer'. If 'allow_negatives' is set to 1 #\n"
+ "# (yes), either negative or positive values can be used. #\n"
+ "# Tuxmath will generate questions for every value in the #\n"
+ "# specified range. The maximum must be greater than or #\n"
+ "# equal to the corresponding minimum for any questions to #\n"
+ "# be generated for that operation. #\n"
+ "############################################################\n\n";
+
+ }
+
+
+ mcdprintf("\nEntering MC_PrintMathOptions()\n");
+
+ /* bail out if no struct */
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMath Options struct does not exist!\n");
+ return;
+ }
+
+ for (i = 0; i < NOPTS; ++i)
+ {
+ if (verbose && vcomments[i] != NULL)
+ fprintf(fp, "%s", vcomments[i]);
+ fprintf(fp, "%s = %d\n", MC_OPTION_TEXT[i], math_opts->iopts[i]);
+ }
+ mcdprintf("\nLeaving MC_PrintMathOptions()\n");
+}
+
+
+
+int MC_PrintQuestionList(FILE* fp)
+{
+ if (fp && question_list)
+ {
+ print_list(fp, question_list);
+ return 1;
+ }
+ else
+ {
+ fprintf(stderr, "\nFile pointer and/or question list invalid\n");
+ return 0;
+ }
+}
+
+int MC_PrintWrongList(FILE* fp)
+{
+ if (!fp)
+ {
+ fprintf(stderr, "File pointer invalid\n");
+ return 0;
+ }
+
+ if (wrong_quests)
+ {
+ print_list(fp, wrong_quests);
+ }
+ else
+ {
+ fprintf(fp, "\nNo wrong questions!\n");
+ }
+
+ return 1;
+}
+
+
+int MC_StartingListLength(void)
+{
+ return starting_length;
+}
+
+
+int MC_WrongListLength(void)
+{
+ return list_length(wrong_quests);
+}
+
+int MC_NumAnsweredCorrectly(void)
+{
+ return answered_correctly;
+}
+
+
+int MC_NumNotAnsweredCorrectly(void)
+{
+ return answered_wrong;
+}
+
+
+/* Report the median time per question */
+float MC_MedianTimePerQuestion(void)
+{
+ if (length_time_per_question_list == 0)
+ return 0;
+
+ qsort(time_per_question_list,length_time_per_question_list,sizeof(float),floatCompare);
+ return time_per_question_list[length_time_per_question_list/2];
+}
+
+
+
+
+/* Implementation of "private methods" - (cannot be called from outside
+of this file) */
+
+
+
+/* Resets negative values to zero - used when allow_negatives deselected. */
+void clear_negatives(void)
+{
+ int i;
+ for (i = MIN_AUGEND; i <= MAX_TYPING_NUM; ++i)
+ if (math_opts->iopts[i]< 0)
+ math_opts->iopts[i]= 0;
+}
+
+// /* this is used by generate_list to see if a possible question */
+// /* meets criteria to be added to the list or not: */
+// int validate_question(int n1, int n2, int n3)
+// {
+// /* make sure none of values exceeds max_answer using absolute */
+// /* value comparison: */
+// if (abs_value(n1) > abs_value(math_opts->iopts[MAX_ANSWER])
+// || abs_value(n2) > abs_value(math_opts->iopts[MAX_ANSWER])
+// || abs_value(n3) > abs_value(math_opts->iopts[MAX_ANSWER]))
+// {
+// return 0;
+// }
+// /* make sure none of values are negative if negatives not allowed: */
+// if (!math_opts->iopts[ALLOW_NEGATIVES])
+// {
+// if (n1 < 0 || n2 < 0 || n3 < 0)
+// {
+// return 0;
+// }
+// }
+// return 1;
+// }
+
+#if 0 //this code is probably on the way out...
+/* create a new node and return a pointer to it */
+MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f)
+{
+ MC_MathQuestion* ptr = NULL;
+
+ ptr = (MC_MathQuestion*)malloc(sizeof(MC_MathQuestion));
+
+ if (!ptr)
+ {
+ fprintf(stderr, "create_node() - malloc() failed!\n");
+ return NULL;
+ }
+
+ ptr->card = MC_AllocateFlashcard();
+ ptr->next = NULL;
+ ptr->previous = NULL;
+
+ snprintf(ptr->card.formula_string, max_formula_size, "%d %c %d = ?",
+ n1, op < MC_NUM_OPERS ? operchars[op] : '\0', n2);
+ snprintf(ptr->card.answer_string, max_formula_size, "%d", ans);
+ ptr->card.difficulty = 25 * (op + 1);
+
+
+ /* ptr should now point to a properly constructed node: */
+ return ptr;
+}
+#endif
+
+MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard)
+{
+ MC_MathQuestion* ret = allocate_node();
+ copy_card(flashcard, &(ret->card));
+ return ret;
+}
+
+// /* FIXME take care of strings */
+// /* this one copies the contents, including pointers; both nodes must be allocated */
+// int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy)
+// {
+// if (!original)
+// {
+// fprintf(stderr, "\nIn copy_node(): invalid 'original' pointer arg.\n");
+// return 0;
+// }
+// if (!copy)
+// {
+// fprintf(stderr, "\nIn copy_node(): invalid 'copy' pointer arg.\n");
+// return 0;
+// }
+//
+// copy_card(&(original->card), &(copy->card) );
+//
+// copy->next = original->next;
+// copy->previous = original->previous;
+// copy->randomizer = original->randomizer;
+// return 1;
+// }
+
+
+
+
+/* this puts the node into the list AFTER the node pointed to by current */
+/* and returns a pointer to the top of the modified list */
+MC_MathQuestion* insert_node(MC_MathQuestion* first,
+ MC_MathQuestion* current,
+ MC_MathQuestion* new_node)
+{
+ /* return pointer to list unchanged if new_node doesn't exist*/
+ if (!new_node)
+ return first;
+ /* if current doesn't exist, new_node is first */
+ if (!current)
+ {
+ new_node->previous = 0;
+ new_node->next =0;
+ first = new_node;
+ return first;
+ }
+
+ if (current->next) /* avoid error if at end of list */
+ current->next->previous = new_node;
+ new_node->next = current->next;
+ current->next = new_node;
+ new_node->previous = current;
+ return first;
+}
+
+
+
+/* adds the new node to the end of the list */
+MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node)
+{
+ MC_MathQuestion* ptr;
+ /* return pointer to list unchanged if new_node doesn't exist*/
+ if (!new_node)
+ {
+ return list;
+ }
+
+ /* if list does not exist, new_node is the first (and only) node */
+ if (!list)
+ {
+ return new_node;
+ }
+ /* otherwise, go to end of list */
+ ptr = list;
+ while (ptr->next)
+ {
+ ptr = ptr->next;
+ }
+
+ ptr->next = new_node;
+ new_node->previous = ptr;
+ new_node->next = 0;
+ return list;
+}
+
+
+
+/* this takes the node out of the list but does not delete it */
+/* and returns a pointer to the top of the modified list */
+MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n)
+{
+ if (!n || !first)
+ return first;
+ /* special case if first node being removed */
+ if (n == first)
+ first = first->next;
+
+ if (n->previous)
+ n->previous->next = n->next;
+ if (n->next)
+ n->next->previous = n->previous;
+ n->previous = 0;
+ n->next = 0;
+ return first;
+}
+
+
+
+/* frees memory for entire list and returns null pointer */
+MC_MathQuestion* delete_list(MC_MathQuestion* list)
+{
+ MC_MathQuestion* tmp_ptr;
+ while (list)
+ {
+ tmp_ptr = list->next;
+ free_node (list);
+ list = tmp_ptr;
+ }
+ return list;
+}
+
+
+
+void print_list(FILE* fp, MC_MathQuestion* list)
+{
+ if (!list)
+ {
+ fprintf(fp, "\nprint_list(): list empty or pointer invalid\n");
+ return;
+ }
+
+ MC_MathQuestion* ptr = list;
+ while (ptr)
+ {
+ fprintf(stderr, "%s\n", ptr->card.formula_string);
+ ptr = ptr->next;
+ }
+}
+
+
+void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length)
+{
+ if (!vect)
+ {
+ fprintf(fp, "\nprint_vect_list(): list empty or pointer invalid\n");
+ return;
+ }
+
+ int i = 0;
+ mcdprintf("Entering print_vect_list()\n");
+ for(i = 0; i < length; i++)
+ fprintf(fp, "%s\n", vect[i]->card.formula_string);
+
+ mcdprintf("Leaving print_vect_list()\n");
+}
+
+
+
+#ifdef MC_DEBUG
+void print_card(MC_FlashCard card)
+{
+ printf("\nprint_card():\n");
+ printf("question_id: %d\nformula_string: %s\nanswer_string: %s\ndifficulty: %d\n\n",
+ card.question_id,
+ card.formula_string,
+ card.answer_string,
+ card.difficulty);
+}
+
+/* This sends the values of all "global" counters and the */
+/* lengths of the question lists to stdout - for debugging */
+void print_counters(void)
+{
+ printf("\nquest_list_length = \t%d", quest_list_length);
+ printf("\nlist_length(question_list) = \t%d", list_length(question_list));
+ printf("\nstarting_length = \t%d", starting_length);
+ printf("\nunanswered = \t%d", unanswered);
+ printf("\nanswered_correctly = \t%d", answered_correctly);
+ printf("\nanswered_wrong = \t%d", answered_wrong);
+ printf("\nlist_length(wrong_quests) = \t%d", list_length(wrong_quests));
+ printf("\nquestions_pending = \t%d", questions_pending);
+ printf("\nlist_length(active_quests) = \t%d", list_length(active_quests));
+}
+
+// /* a "copy constructor", so to speak */
+// /* FIXME should properly return newly allocated list if more than one node DSB */
+// MC_MathQuestion* create_node_copy(MC_MathQuestion* other)
+// {
+// MC_MathQuestion* ret = allocate_node();
+// if (ret)
+// copy_card(&(other->card), &(ret->card) );
+// return ret;
+// }
+//
+// /* FIXME take care of strings */
+//
+// MC_FlashCard create_card_from_node(MC_MathQuestion* node)
+// {
+// MC_FlashCard fc;
+// if (!node)
+// return DEFAULT_CARD;
+// fc = MC_AllocateFlashcard();
+// copy_card(&(node->card), &fc);
+// return fc;
+// }
+#endif
+
+
+
+
+int list_length(MC_MathQuestion* list)
+{
+ int length = 0;
+ while (list)
+ {
+ length++;
+ list = list->next;
+ }
+ return length;
+}
+
+
+
+
+
+
+/* This is a new implementation written in an attempt to avoid */
+/* the O(n^2) performance problems seen with the old randomization */
+/* function. The list is created as a vector, but is for now still */
+/* made a linked list to minimize changes needed elsewhere. */
+/* The argument is a pointer to the top of the old list. This extra */
+/* level of indirection allows the list to be shuffled "in-place". */
+/* The function returns 1 if successful, 0 on errors. */
+
+static int randomize_list(MC_MathQuestion** old_list)
+{
+ MC_MathQuestion* old_tmp = *old_list;
+ MC_MathQuestion** tmp_vect = NULL;
+
+ int i = 0;
+ if (!old_list || !*old_list) //invalid/empty list
+ return 0;
+
+ int old_length = list_length(old_tmp);
+
+ /* set random seed: */
+ srand(time(0));
+
+
+ /* Allocate vector and set ptrs to nodes in old list: */
+
+ /* Allocate a list of pointers, not space for the nodes themselves: */
+ tmp_vect = (MC_MathQuestion**)malloc(sizeof(MC_MathQuestion*) * old_length);
+ /* Set each pointer in the vector to the corresponding node: */
+ for (i = 0; i < old_length; i++)
+ {
+ tmp_vect[i] = old_tmp;
+ tmp_vect[i]->randomizer = rand();
+ old_tmp = old_tmp->next;
+ }
+
+ /* Now simply sort on 'tmp_vect[i]->randomizer' to shuffle list: */
+ qsort(tmp_vect, old_length,
+ sizeof(MC_MathQuestion*),
+ comp_randomizer);
+
+ /* Re-create pointers to provide linked-list functionality: */
+ /* (stop at 'old_length-1' because we dereference tmp_vect[i+1]) */
+ for(i = 0; i < old_length - 1; i++)
+ {
+ if (!tmp_vect[i])
+ {
+ fprintf(stderr, "Invalid pointer!\n");
+ return 0;
+ }
+ tmp_vect[i]->next = tmp_vect[i+1];
+ tmp_vect[i+1]->previous = tmp_vect[i];
+ }
+ /* Handle end cases: */
+ tmp_vect[0]->previous = NULL;
+ tmp_vect[old_length-1]->next = NULL;
+
+ /* Now arrange for arg pointer to indirectly point to first element! */
+ *old_list = tmp_vect[0];
+ free(tmp_vect);
+ return 1;
+}
+
+
+
+/* This is needed for qsort(): */
+int comp_randomizer (const void* a, const void* b)
+{
+
+ int int1 = (*(const struct MC_MathQuestion **) a)->randomizer;
+ int int2 = (*(const struct MC_MathQuestion **) b)->randomizer;
+
+ if (int1 > int2)
+ return 1;
+ else if (int1 == int2)
+ return 0;
+ else
+ return -1;
+}
+
+MC_MathQuestion* pick_random(int length, MC_MathQuestion* list)
+{
+ int i;
+ int rand_node;
+
+ /* set random seed DSB */
+ srand(time(0));
+
+ /* if length is zero, get out to avoid divide-by-zero error */
+ if (0 == length)
+ {
+ return list;
+ }
+
+ rand_node = rand() % length;
+
+ for (i=1; i < rand_node; i++)
+ {
+ if (list)
+ list = list->next;
+ }
+
+ return list;
+}
+
+/* compares fields other than pointers */
+int compare_node(MC_MathQuestion* first, MC_MathQuestion* other)
+{
+ if (!first || !other)
+ return 0;
+ if (compare_card(&(first->card), &(first->card) ) ) //cards are equal
+ return 1;
+ else
+ return 0;
+}
+
+/* check to see if list already contains an identical node */
+int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr)
+{
+ if (!list || !ptr)
+ return 0;
+
+ while (list)
+ {
+ if (compare_node(list, ptr))
+ return 1;
+ list = list->next;
+ }
+ return 0;
+}
+
+// /* to prevent option settings in math_opts from getting set to */
+// /* values other than 0 or 1 */
+// int int_to_bool(int i)
+// {
+// if (i)
+// return 1;
+// else
+// return 0;
+// }
+
+// /* prevent values from getting into math_opts that are outside */
+// /* the range that can be handled by the program (i.e. more */
+// /* than three digits; also disallow negatives if that has been */
+// /* selected. */
+// int sane_value(int i)
+// {
+// if (i > MC_GLOBAL_MAX)
+// i = MC_GLOBAL_MAX;
+// else if (i < -MC_GLOBAL_MAX)
+// i = -MC_GLOBAL_MAX;
+//
+// if (i < 0
+// && math_opts
+// && !math_opts->iopts[ALLOW_NEGATIVES])
+// {
+// i = 0;
+// }
+//
+// return i;
+// }
+
+// int abs_value(int i)
+// {
+// if (i > 0)
+// return i;
+// else
+// return -i;
+// }
+
+int log10i(int i) //base 10 logarithm for ints
+{
+ int j;
+ for (j = 0; i > 0; i /= 10, ++j);
+ return j;
+}
+
+/* Compares two floats (needed for sorting in MC_MedianTimePerQuestion) */
+int floatCompare(const void *v1,const void *v2)
+{
+ float f1,f2;
+
+ f1 = *((float *) v1);
+ f2 = *((float *) v2);
+
+ if (f1 < f2)
+ return -1;
+ else if (f1 > f2)
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/****************************************************
+Functions for new mathcards architecture
+****************************************************/
+
+void copy_card(const MC_FlashCard* src, MC_FlashCard* dest)
+{
+ if (!src || !dest)
+ return;
+ mcdprintf("Copying '%s' to '%s', ", src->formula_string,dest->formula_string);
+ mcdprintf("copying '%s' to '%s'\n", src->answer_string, dest->answer_string);
+ strncpy(dest->formula_string, src->formula_string, MC_FORMULA_LEN);
+ strncpy(dest->answer_string, src->answer_string, MC_ANSWER_LEN);
+ mcdprintf("Card is: '%s', '%s'\n", dest->formula_string, dest->answer_string);
+ dest->answer = src->answer;
+ dest->difficulty = src->difficulty;
+ dest->question_id = src->question_id;
+}
+
+void free_node(MC_MathQuestion* mq) //no, not that freenode.
+{
+ if (!mq)
+ return;
+ MC_FreeFlashcard(&(mq->card) );
+ free(mq);
+}
+
+MC_MathQuestion* allocate_node()
+{
+ MC_MathQuestion* ret = NULL;
+ ret = malloc(sizeof(MC_MathQuestion) );
+ if (!ret)
+ {
+ printf("Could not allocate space for a new node!\n");
+ return NULL;
+ }
+
+ ret->card = MC_AllocateFlashcard();
+ ret->next = ret->previous = NULL;
+
+ return ret;
+}
+
+/*
+The function that does the central dirty work pertaining to flashcard
+creation. Extensible to just about any kind of math problem, perhaps
+with the exception of those with multiple answers, such as "8 + 2 > ?"
+Simply specify how the problem is presented to the user, and the
+answer the game should look for, as strings.
+*/
+MC_FlashCard generate_random_flashcard(void)
+{
+ int num;
+ int length;
+ MC_ProblemType pt;
+ MC_FlashCard ret;
+ static int generate_random_flashcard_id=0;
+
+ generate_random_flashcard_id+=1;
+ mcdprintf("Entering generate_random_flashcard()\n");
+ mcdprintf("%d\n",generate_random_flashcard_id);
+ do
+ pt = rand() % MC_NUM_PTYPES;
+ while ( (pt == MC_PT_TYPING && !MC_GetOpt(TYPING_PRACTICE_ALLOWED) ) ||
+ (pt == MC_PT_ARITHMETIC && !MC_GetOpt(ADDITION_ALLOWED) &&
+ !MC_GetOpt(SUBTRACTION_ALLOWED) &&
+ !MC_GetOpt(MULTIPLICATION_ALLOWED) &&
+ !MC_GetOpt(DIVISION_ALLOWED) ) ||
+ (pt == MC_PT_COMPARISON && !MC_GetOpt(COMPARISON_ALLOWED) )
+ );
+
+ if (pt == MC_PT_TYPING) //typing practice
+ {
+ mcdprintf("Generating typing question\n");
+ ret = MC_AllocateFlashcard();
+ num = rand() % (MC_GetOpt(MAX_TYPING_NUM)-MC_GetOpt(MIN_TYPING_NUM) + 1)
+ + MC_GetOpt(MIN_TYPING_NUM);
+ snprintf(ret.formula_string, MC_FORMULA_LEN, "%d", num);
+ snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", num);
+ ret.answer = num;
+ ret.difficulty = 10;
+ ret.question_id=generate_random_flashcard_id;
+ }
+ else //if (pt == MC_PT_ARITHMETIC)
+ {
+ mcdprintf("Generating arithmetic question");
+ length = rand() % (MC_GetOpt(MAX_FORMULA_NUMS) -
+ MC_GetOpt(MIN_FORMULA_NUMS) + 1) //avoid div by 0
+ + MC_GetOpt(MIN_FORMULA_NUMS);
+ mcdprintf(" of length %d", length);
+ ret = generate_random_ooo_card_of_length(length, 1);
+ #ifdef MC_DEBUG
+ print_card(ret);
+ #endif
+ }
+ //TODO comparison problems (e.g. "6 ? 9", "<")
+
+ mcdprintf("Exiting generate_random_flashcard()\n");
+
+ return ret;
+}
+
+/*
+Recursively generate an order of operations problem. Hopefully this won't
+raise performance issues. Difficulty is calculated based on the length of
+the formula and on the operators used. Problems have a 'base' difficulty of
+1 for binary operations, 3 for 3 numbers, 6, 10, etc. Each operator adds to
+the score: 0, 1, 2, and 3 respectively for addition, subtraction,
+multiplication and division.If reformat is 0, FORMAT_ANS_LAST will be used,
+otherwise a format is chosen at random.
+*/
+MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat)
+{
+ int format = 0;
+ int r1 = 0;
+ int r2 = 0;
+ int ans = 0;
+ char tempstr[MC_FORMULA_LEN];
+ MC_FlashCard ret;
+ MC_Operation op;
+ static int id = 0;
+
+ id += 1;
+ mcdprintf(".");
+ if (length > MAX_FORMULA_NUMS)
+ return DEFAULT_CARD;
+ if (length <= 2)
+ {
+ mcdprintf("\n");
+ ret = MC_AllocateFlashcard();
+ for (op = rand() % MC_NUM_OPERS; //pick a random operation
+ MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
+ op = rand() % MC_NUM_OPERS);
+
+ mcdprintf("Operation is %c\n", operchars[op]);
+ /*
+ if (op == MC_OPER_ADD)
+ {
+ r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
+ r2 = rand() % (math_opts->iopts[MAX_ADDEND] - math_opts->iopts[MIN_ADDEND] + 1) + math_opts->iopts[MIN_ADDEND];
+ ans = r1 + r2;
+ }
+ else if (op == MC_OPER_SUB)
+ {
+ r1 = rand() % (math_opts->iopts[MAX_MINUEND] - math_opts->iopts[MIN_MINUEND] + 1) + math_opts->iopts[MIN_MINUEND];
+ r2 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
+ ans = r1 - r2;
+ }
+ else if (op == MC_OPER_MULT)
+ {
+ r1 = rand() % (math_opts->iopts[MAX_MULTIPLIER] - math_opts->iopts[MIN_MULTIPLIER] + 1) + math_opts->iopts[MIN_MULTIPLIER];
+ r2 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_MULTIPLICAND];
+ ans = r1 * r2;
+ }
+ else if (op == MC_OPER_DIV)
+ {
+ ans = rand() % (math_opts->iopts[MAX_QUOTIENT] - math_opts->iopts[MIN_QUOTIENT] + 1) + math_opts->iopts[MIN_QUOTIENT];
+ r2 = rand() % (math_opts->iopts[MAX_DIVISOR] - math_opts->iopts[MIN_DIVISOR] + 1) + math_opts->iopts[MIN_DIVISOR];
+ if (r2 == 0)
+ r2 = 1;
+ r1 = ans * r2;
+ }
+ */
+ if (op > MC_OPER_DIV || op < MC_OPER_ADD)
+ {
+ mcdprintf("Invalid operator: value %d\n", op);
+ return DEFAULT_CARD;
+ }
+ //choose two numbers in the proper range and get their result
+
+ else do
+ {
+ r1 = rand() % (math_opts->iopts[MAX_AUGEND+4*op] - math_opts->iopts[MIN_AUGEND+4*op] + 1) + math_opts->iopts[MIN_AUGEND+4*op];
+ r2 = rand() % (math_opts->iopts[MAX_ADDEND+4*op] - math_opts->iopts[MIN_ADDEND+4*op] + 1) + math_opts->iopts[MIN_ADDEND+4*op];
+
+ if (op == MC_OPER_ADD)
+ ans = r1 + r2;
+ if (op == MC_OPER_SUB)
+ ans = r1 - r2;
+ if (op == MC_OPER_MULT)
+ ans = r1 * r2;
+ if (op == MC_OPER_DIV)
+ {
+ if (r2 == 0)
+ r2 = 1;
+ ret.difficulty = r1;
+ r1 *= r2;
+ ans = ret.difficulty;
+ }
+ } while ( (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES)) || ans > MC_GetOpt(MAX_ANSWER) );
+
+
+ mcdprintf("Constructing answer_string\n");
+ snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", ans);
+ mcdprintf("Constructing formula_string\n");
+ snprintf(ret.formula_string, MC_FORMULA_LEN, "%d %c %d",
+ r1, operchars[op], r2);
+ ret.answer = ans;
+ ret.difficulty = op + 1;
+
+ }
+ else //recurse
+ {
+ ret = generate_random_ooo_card_of_length(length - 1, 0);
+
+ if (strchr(ret.formula_string, '+') || strchr(ret.formula_string, '-') )
+ {
+ //if the expression has addition or subtraction, we can't assume that
+ //introducing multiplication or division will produce a predictable
+ //result, so we'll limit ourselves to more addition/subtraction
+ for (op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB;
+ MC_GetOpt(op + ADDITION_ALLOWED) == 0;
+ op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB);
+
+ }
+ else
+ {
+ //the existing expression can be treated as a number in itself, so we
+ //can do anything to it and be confident of the result.
+ for (op = rand() % MC_NUM_OPERS; //pick a random operation
+ MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
+ op = rand() % MC_NUM_OPERS);
+ }
+ mcdprintf("Next operation is %c,", operchars[op]);
+
+ //pick the next operand
+ if (op == MC_OPER_ADD)
+ {
+ r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
+ ret.answer += r1;
+ }
+ else if (op == MC_OPER_SUB)
+ {
+ r1 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
+ ret.answer -= r1;
+ }
+ else if (op == MC_OPER_MULT)
+ {
+ r1 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_AUGEND];
+ ret.answer *= r1;
+ }
+ else if (op == MC_OPER_DIV)
+ {
+ r1 = find_divisor(ret.answer);
+ ret.answer /= r1;
+ }
+ else
+ {
+ ; //invalid operator
+ }
+ mcdprintf(" operand is %d\n", r1);
+ mcdprintf("Answer: %d\n", ret.answer);
+
+ //next append or prepend the new number (might need optimization)
+ if (op == MC_OPER_SUB || op == MC_OPER_DIV || //noncommutative, append only
+ rand() % 2)
+ {
+ snprintf(tempstr, MC_FORMULA_LEN, "%s %c %d", //append
+ ret.formula_string, operchars[op], r1);
+ strncpy(ret.formula_string, tempstr, MC_FORMULA_LEN);
+ }
+ else //we're prepending
+ {
+ snprintf(tempstr, MC_FORMULA_LEN, "%d %c %s", //append
+ r1, operchars[op], ret.formula_string);
+ strncpy(ret.formula_string, tempstr, MC_FORMULA_LEN);
+ }
+
+ //finally update the answer and score
+ snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", ret.answer);
+ ret.difficulty += (length - 1) + op;
+ }
+
+ if (reformat)
+ {
+ mcdprintf("Reformatting...\n");
+ do {
+ format = rand() % MC_NUM_FORMATS;
+ } while (!MC_GetOpt(FORMAT_ANSWER_LAST + format) &&
+ !MC_GetOpt(FORMAT_ADD_ANSWER_LAST + op * 3 + format) );
+
+ strncat(ret.formula_string, " = ?", MC_FORMULA_LEN - strlen(ret.formula_string) );
+ reformat_arithmetic(&ret, format );
+ }
+ ret.question_id=id;
+ return ret;
+}
+
+
+
+MC_MathQuestion* generate_list(void)
+{
+ int i, j;
+ int length = MC_GetOpt(AVG_LIST_LENGTH);
+ int cl; //raw length
+ double r1, r2, delta, var; //randomizers for list length
+ MC_MathQuestion* list = NULL;
+ MC_MathQuestion* end_of_list = NULL;
+ MC_MathQuestion* tnode = NULL;
+
+#ifdef MC_DEBUG
+ MC_PrintMathOptions(stdout, 0);
+#endif
+
+ if (!(MC_GetOpt(ARITHMETIC_ALLOWED) ||
+ MC_GetOpt(TYPING_PRACTICE_ALLOWED) ||
+ MC_GetOpt(COMPARISON_ALLOWED) ) )
+ return NULL;
+
+ //FIXME - remind me, why are we doing this??
+ //randomize list length by a "bell curve" centered on average
+ if (length && MC_GetOpt(VARY_LIST_LENGTH) )
+ {
+ r1 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
+ r2 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
+ mcdprintf("Randoms chosen: %5f, %5f\n", r1, r2);
+ delta = sqrt(-2 * log(r1) ) * cos(2 * PI_VAL * r2); //standard normal dist.
+ var = length / 10.0; //variance
+ delta = delta * var;
+ mcdprintf("Delta of average is %5f\n", delta);
+ length += delta;
+ if (length < 0)
+ length = 1; //just in case...
+ }
+
+ if (MC_GetOpt(COMPREHENSIVE)) //generate all
+ {
+ int num_valid_questions; //How many questions the COMPREHENSIVE list specifies
+ int cycles_needed; //How many times we need to generate it to get enough
+
+ num_valid_questions = calc_num_valid_questions();
+ if(num_valid_questions == 0)
+ {
+ fprintf(stderr, "generate_list() - no valid questions\n");
+ return NULL;
+ }
+
+ cycles_needed = length/num_valid_questions;
+
+ if((cycles_needed * num_valid_questions) < length)
+ cycles_needed++;
+
+ mcdprintf("In generate_list() - COMPREHENSIVE method requested\n");
+ mcdprintf("num_valid_questions = %d\t cycles_needed = %d\n",
+ num_valid_questions, cycles_needed);
+
+ for (i = MC_PT_TYPING; i < MC_NUM_PTYPES; ++i)
+ {
+ if (!MC_GetOpt(i + TYPING_PRACTICE_ALLOWED))
+ continue;
+ for (j = 0; j < cycles_needed; j++)
+ list = add_all_valid(i, list, &end_of_list);
+ }
+
+
+ if (MC_GetOpt(RANDOMIZE) )
+ {
+ mcdprintf("Randomizing list\n");
+ randomize_list(&list);
+ }
+
+ if (length)
+ {
+ cl = list_length(list);
+ // NOTE this should no longer happen - we run the COMPREHENSIVE
+ // generation until we have enough questions.
+ if (length > cl) //if not enough questions, pad out with randoms
+ {
+ mcdprintf("Padding out list from %d to %d questions\n", cl, length);
+ for (i = cl; i < length; ++i)
+ {
+ tnode = malloc(sizeof(MC_MathQuestion) );
+ if(!tnode)
+ {
+ fprintf(stderr, "In generate_list() - allocation failed!\n");
+ delete_list(list);
+ return NULL;
+ }
+
+ tnode->card = generate_random_flashcard();
+ list = insert_node(list, end_of_list, tnode);
+ end_of_list = tnode;
+// mcdprintf("%d.", list_length(list) );
+ }
+ }
+ else if (length < cl) //if too many questions, chop off tail end of list
+ {
+ mcdprintf("Cutting list to %d questions\n", length);
+ end_of_list = find_node(list, length);
+ delete_list(end_of_list->next);
+ end_of_list->next = NULL;
+ }
+ }
+ }
+
+ /* Here we are just generating random questions, one at a */
+ /* time until we have enough */
+ else
+ {
+ mcdprintf("In generate_list() - COMPREHENSIVE method NOT requested\n");
+
+ for (i = 0; i < length; ++i)
+ {
+ tnode = malloc(sizeof(MC_MathQuestion) );
+ if(!tnode)
+ {
+ fprintf(stderr, "In generate_list() - allocation failed!\n");
+ delete_list(list);
+ return NULL;
+ }
+
+ tnode->card = generate_random_flashcard();
+ list = insert_node(list, end_of_list, tnode);
+ end_of_list = tnode;
+ }
+ }
+ return list;
+}
+
+/* NOTE - returns 0 (i.e. "false") if *identical*, and */
+/* 1 (i.e. "true") if *different* - counterintuitive, */
+/* but same behavior as e.g. strcmp() */
+
+static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b)
+{
+ if (strncmp(a->formula_string, b->formula_string, MC_FORMULA_LEN) )
+ return 1;
+ if (strncmp(a->answer_string, b->answer_string, MC_ANSWER_LEN) )
+ return 1;
+ if (a->answer != b->answer);
+ return 1;
+
+ return 0; //the cards are identical
+}
+
+/* Public functions */
+
+/* allocate space for an MC_Flashcard */
+MC_FlashCard MC_AllocateFlashcard(void)
+{
+ MC_FlashCard ret;
+
+//NOTE strings now simply hard-coded to MC_FORMULA_LEN (= 40) and
+//MC_ANSWER_LEN (= 5) instead of tailoring them to save a few bytes - DSB
+// mcdprintf("Allocating %d + %d bytes for flashcard\n",
+// max_formula_size + 1, max_answer_size + 1);
+// ret.formula_string = malloc( (max_formula_size + 1) * sizeof(char));
+// ret.answer_string = malloc( (max_answer_size + 1) * sizeof(char));
+// if (!ret.formula_string || !ret.answer_string)
+// {
+// free(ret.formula_string);
+// free(ret.answer_string);
+// printf("Couldn't allocate space for a new flashcard!\n");
+// ret = DEFAULT_CARD;
+// }
+ return ret;
+}
+
+//Now a no-op - MC_FlashCard no longer has dynamically allocated strings
+void MC_FreeFlashcard(MC_FlashCard* fc)
+{
+ return;
+// if (!fc)
+// return;
+// // mcdprintf("Freeing formula_string\n");
+// if (fc->formula_string)
+// {
+// free(fc->formula_string);
+// fc->formula_string = NULL;
+// }
+// // mcdprintf("Freeing answer_string\n");
+// if (fc->answer_string)
+// {
+// free(fc->answer_string);
+// fc->answer_string = NULL;
+// }
+}
+
+unsigned int MC_MapTextToIndex(const char* text)
+{
+ int i;
+ for (i = 0; i < NOPTS; ++i)
+ {
+ if (!strcasecmp(text, MC_OPTION_TEXT[i]) )
+ return i;
+ }
+ mcdprintf("'%s' isn't a math option\n", text);
+ return NOT_VALID_OPTION;
+}
+
+
+//TODO more intuitive function names for access by index vs. by text
+void MC_SetOpt(unsigned int index, int val)
+{
+ if (index >= NOPTS)
+ {
+ mcdprintf("Invalid math option index: %d\n", index);
+ return;
+ }
+
+ /* Do some sanity checks before we throw val into the struct: */
+ switch(index)
+ {
+ /* All the booleans must be 0 or 1: */
+ case PLAY_THROUGH_LIST:
+ case REPEAT_WRONGS:
+ case ALLOW_NEGATIVES:
+ case FORMAT_ANSWER_LAST:
+ case FORMAT_ANSWER_FIRST:
+ case FORMAT_ANSWER_MIDDLE:
+ case FORMAT_ADD_ANSWER_LAST:
+ case FORMAT_ADD_ANSWER_FIRST:
+ case FORMAT_ADD_ANSWER_MIDDLE:
+ case FORMAT_SUB_ANSWER_LAST:
+ case FORMAT_SUB_ANSWER_FIRST:
+ case FORMAT_SUB_ANSWER_MIDDLE:
+ case FORMAT_MULT_ANSWER_LAST:
+ case FORMAT_MULT_ANSWER_FIRST:
+ case FORMAT_MULT_ANSWER_MIDDLE:
+ case FORMAT_DIV_ANSWER_LAST:
+ case FORMAT_DIV_ANSWER_FIRST:
+ case FORMAT_DIV_ANSWER_MIDDLE:
+ case ADDITION_ALLOWED:
+ case SUBTRACTION_ALLOWED:
+ case MULTIPLICATION_ALLOWED:
+ case DIVISION_ALLOWED:
+ case TYPING_PRACTICE_ALLOWED:
+ case ARITHMETIC_ALLOWED:
+ case COMPARISON_ALLOWED:
+ case RANDOMIZE:
+ case COMPREHENSIVE:
+ case VARY_LIST_LENGTH:
+ {
+ /* Reset all non-zero values to one: */
+ if(val)
+ {
+ if(val != 1)
+ {
+ fprintf(stderr, "Warning - parameter %s with invalid value %d, "
+ "resetting to 1\n", MC_OPTION_TEXT[index], val);
+ val = 1;
+ }
+ }
+ break;
+ }
+
+ /* Parameters concerning numbers of questions */
+ /* must be greater than or equal to zero: */
+ /* TODO some additional checks would make sense */
+ case QUESTION_COPIES:
+ case COPIES_REPEATED_WRONGS:
+ case MAX_QUESTIONS:
+ case MAX_FORMULA_NUMS:
+ case MIN_FORMULA_NUMS:
+ case AVG_LIST_LENGTH:
+ {
+ /* Reset all negative values to zero: */
+ if(val < 0)
+ {
+ fprintf(stderr, "Warning - parameter %s with invalid value %d, "
+ "resetting to 0\n", MC_OPTION_TEXT[index], val);
+ val = 0;
+ }
+ break;
+ }
+
+ /* Operand values - make sure they are in displayable range */
+ /* i.e. -999 to 999 */
+ case MAX_ANSWER:
+ case MIN_AUGEND:
+ case MAX_AUGEND:
+ case MIN_ADDEND:
+ case MAX_ADDEND:
+ case MIN_MINUEND:
+ case MAX_MINUEND:
+ case MIN_SUBTRAHEND:
+ case MAX_SUBTRAHEND:
+ case MIN_MULTIPLIER:
+ case MAX_MULTIPLIER:
+ case MIN_MULTIPLICAND:
+ case MAX_MULTIPLICAND:
+ case MIN_DIVISOR:
+ case MAX_DIVISOR:
+ case MIN_QUOTIENT:
+ case MAX_QUOTIENT:
+ case MIN_TYPING_NUM:
+ case MAX_TYPING_NUM:
+ case MIN_COMPARATOR:
+ case MAX_COMPARATOR:
+ case MIN_COMPARISAND:
+ case MAX_COMPARISAND:
+ {
+ if(val > MC_GLOBAL_MAX)
+ {
+ fprintf(stderr, "Warning - parameter %s with invalid value %d, "
+ "resetting to %d\n", MC_OPTION_TEXT[index],
+ val, MC_GLOBAL_MAX);
+ val = MC_GLOBAL_MAX;
+ }
+
+ if(val < (0 - MC_GLOBAL_MAX))
+ {
+ fprintf(stderr, "Warning - parameter %s with invalid value %d, "
+ "resetting to %d\n", MC_OPTION_TEXT[index],
+ val, (0 - MC_GLOBAL_MAX));
+ val = (0 - MC_GLOBAL_MAX);
+ }
+
+ break;
+ }
+
+ default:
+ fprintf(stderr, "Warning - in MC_SetOpt() - unrecognized index %d\n",
+ index);
+ }
+ /* Should now be safe to put "sanitized" value into struct: */
+ math_opts->iopts[index] = val;
+}
+
+void MC_SetOp(const char* param, int val)
+{
+ MC_SetOpt(MC_MapTextToIndex(param), val);
+}
+
+int MC_GetOpt(unsigned int index)
+{
+ if (index >= NOPTS)
+ {
+ mcdprintf("Invalid option index: %d\n", index);
+ return MC_MATH_OPTS_INVALID;
+ }
+ if (!math_opts)
+ {
+ printf("Invalid options list!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->iopts[index];
+}
+
+int MC_GetOp(const char* param)
+{
+ return MC_GetOpt(MC_MapTextToIndex(param) );
+}
+
+int MC_VerifyOptionListSane(void)
+{
+ return strcmp(MC_OPTION_TEXT[NOPTS], "END_OF_OPTS") == 0;
+}
+
+int MC_MaxFormulaSize(void)
+{
+ return MC_FORMULA_LEN;
+}
+
+int MC_MaxAnswerSize(void)
+{
+ return MC_ANSWER_LEN;
+}
+
+void MC_ResetFlashCard(MC_FlashCard* fc)
+{
+ if (!fc || !fc->formula_string || !fc->answer_string)
+ return;
+ strncpy(fc->formula_string, " ", MC_FORMULA_LEN);
+ strncpy(fc->answer_string, " ", MC_ANSWER_LEN);
+ fc->answer = 0;
+ fc->difficulty = 0;
+}
+
+int MC_FlashCardGood(const MC_FlashCard* fc)
+{
+ return fc && fc->formula_string && fc->answer_string;
+}
+
+int find_divisor(int a)
+{
+ int div = 1; //the divisor to return
+ int realisticpasses = 3; //reasonable time after which a minimum should be met
+ int i;
+ do
+ for (i = 0; i < NPRIMES; ++i) //test each prime
+ if (a % smallprimes[i] == 0) //if it is a prime factor,
+ if (rand() % (i + 1) == 0) //maybe we'll keep it
+ if (div * smallprimes[i] <= MC_GetOpt(MAX_DIVISOR) ) //if we can,
+ div *= smallprimes[i]; //update our real divisor
+ //keep going if the divisor is too small
+ while (div < MC_GetOpt(MIN_DIVISOR) && --realisticpasses);
+
+ return div;
+}
+
+
+//Computes (approximately) the number of questions that will be returned
+//by add_all_valid() as specified by the current options. This does not
+//take into account screening out of invalid questions, such
+//as divide-by-zero and questions like "0 x ? = 0".
+static int calc_num_valid_questions(void)
+{
+ int total_questions = 0;
+ int k = 0;
+ //First add the number of typing questions
+ if (MC_GetOpt(TYPING_PRACTICE_ALLOWED))
+ total_questions += (MC_GetOpt(MAX_TYPING_NUM) - MC_GetOpt(MIN_TYPING_NUM));
+
+ //Now add how many questions we will have for each operation:
+ for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
+ {
+ int num_this_oper = 0;
+ int formats_this_oper = 0;
+
+ if (!MC_GetOpt(k + ADDITION_ALLOWED) )
+ continue;
+
+ //calculate number of ordered pairs of first and second operands:
+ //note the "+ 1" is due to the ranges being inclusive
+ num_this_oper = (MC_GetOpt(MAX_AUGEND + 4 * k) - MC_GetOpt(MIN_AUGEND + 4 * k) + 1)
+ *
+ (MC_GetOpt(MAX_ADDEND + 4 * k) - MC_GetOpt(MIN_ADDEND + 4 * k) + 1);
+ //check what formats are allowed
+ if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
+ formats_this_oper++;
+ if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3))
+ formats_this_oper++;
+ if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
+ formats_this_oper++;
+ //Get total of e.g. addition questions:
+ num_this_oper *= formats_this_oper;
+ //add to overall total:
+ total_questions += num_this_oper;
+ }
+
+ //TODO will also need to count up the COMPARISON questions once
+ //they are implemented
+ {
+ }
+
+ mcdprintf("calc_num_valid_questions():\t%d\n", total_questions);
+ return total_questions;
+}
+
+
+//NOTE end_of_list** needs to be doubly indirect because otherwise the end does not
+//get updated in the calling code
+//NOTE the difficulty is set as add = 1, sub = 2, mult = 3, div = 4, plus a 2 point
+//bonus if the format is a "missing number".
+MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list)
+{
+ int i, j;
+ int ans = 0, tmp;
+ MC_Operation k;
+ MC_MathQuestion* tnode;
+
+ mcdprintf("Entering add_all_valid(%d)\n", pt);
+ mcdprintf("List already has %d questions\n", list_length(list));
+
+ //make sure this problem type is actually allowed
+ if (!MC_GetOpt(pt + TYPING_PRACTICE_ALLOWED) )
+ return list;
+
+ //add all typing questions in range
+ if (pt == MC_PT_TYPING)
+ {
+ mcdprintf("Adding typing...\n");
+ for (i = MC_GetOpt(MIN_TYPING_NUM); i <= MC_GetOpt(MAX_TYPING_NUM); ++i)
+ {
+ mcdprintf("(%d)\n", i);
+ tnode = allocate_node();
+ if(!tnode)
+ {
+ fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
+ delete_list(list);
+ return NULL;
+ }
+
+ snprintf(tnode->card.formula_string, MC_FORMULA_LEN, "%d", i);
+ snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", i);
+ tnode->card.difficulty = 1;
+ list = insert_node(list, *end_of_list, tnode);
+ *end_of_list = tnode;
+ }
+ }
+
+ //add all allowed arithmetic questions
+ else if (MC_PT_ARITHMETIC)
+ {
+ mcdprintf("Adding arithmetic...\n");
+
+ // The k loop iterates through the four arithmetic operations:
+ // k = 0 means addition
+ // k = 1 means subtraction
+ // k = 2 means multiplication
+ // k = 3 means division
+ for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
+ {
+ if (!MC_GetOpt(k + ADDITION_ALLOWED) )
+ continue;
+ mcdprintf("\n*%d*\n", k);
+
+ // The i loop iterates through the first value in the question:
+ for (i = MC_GetOpt(MIN_AUGEND + 4 * k); i <= MC_GetOpt(MAX_AUGEND + 4 * k); ++i)
+ {
+ mcdprintf("\n%d:\n", i);
+
+ // The j loop iterates through the second value in the question:
+ for (j = MC_GetOpt(MIN_ADDEND + 4 * k); j <= MC_GetOpt(MAX_ADDEND + 4 * k); ++j)
+ {
+ // Generate the third number according to the operation.
+ // Although it is called "ans", it will not be the actual
+ // answer if it is a "missing number" type problem
+ // (e.g. "3 x ? = 12")
+ // We also filter out invalid questions here
+ switch (k)
+ {
+ case MC_OPER_ADD:
+ {
+ ans = i + j;
+ // throw anything over MAX_ANSWER
+ if (ans > MC_GetOpt(MAX_ANSWER))
+ continue;
+ break;
+ }
+ case MC_OPER_SUB:
+ {
+ ans = i - j;
+ // throw out negatives if they aren't allowed:
+ if (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES))
+ continue;
+ // throw anything over MAX_ANSWER
+ if (ans > MC_GetOpt(MAX_ANSWER))
+ continue;
+ break;
+ }
+ case MC_OPER_MULT:
+ {
+ ans = i * j;
+ // throw anything over MAX_ANSWER
+ if (ans > MC_GetOpt(MAX_ANSWER))
+ continue;
+ break;
+ }
+ case MC_OPER_DIV:
+ {
+ // throw anything over MAX_ANSWER
+ if (i * j > MC_GetOpt(MAX_ANSWER))
+ continue;
+
+ tmp = i;
+ i *= j;
+ ans = j;
+ j = tmp;
+ break;
+ }
+ default:
+ fprintf(stderr, "Unrecognized operation type: %d\n", k);
+ continue;
+ }
+
+ mcdprintf("Generating: %d %c %d = %d\n", i, operchars[k], j, ans);
+
+ //add each format, provided it's allowed in general and for this op
+
+ // Questions like "a + b = ?"
+ if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
+ {
+ // Avoid division by zero:
+ if (k == MC_OPER_DIV && j == 0)
+ {
+ // need to restore i and j to original values so loop works:
+ j = ans;
+ i = tmp;
+ continue;
+ }
+
+ tnode = allocate_node();
+ if(!tnode)
+ {
+ fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
+ delete_list(list);
+ return NULL;
+ }
+
+ snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", ans);
+ snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
+ "%d %c %d = ?", i, operchars[k], j);
+ tnode->card.difficulty = k + 1;
+ list = insert_node(list, *end_of_list, tnode);
+ *end_of_list = tnode;
+ }
+
+
+ // Questions like "? + b = c"
+ if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3) )
+ {
+ // Avoid questions with indeterminate answer:
+ // e.g. "? x 0 = 0"
+ if (k == MC_OPER_MULT && j == 0)
+ {
+ continue;
+ }
+ // Avoid division by zero:
+ if (k == MC_OPER_DIV && j == 0)
+ {
+ // need to restore i and j to original values so loop works:
+ j = ans;
+ i = tmp;
+ continue;
+ }
+
+ tnode = allocate_node();
+ if(!tnode)
+ {
+ fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
+ delete_list(list);
+ return NULL;
+ }
+
+ snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", i);
+ snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
+ "? %c %d = %d", operchars[k], j, ans);
+ tnode->card.difficulty = k + 3;
+ list = insert_node(list, *end_of_list, tnode);
+ *end_of_list = tnode;
+ }
+
+
+ // Questions like "a + ? = c"
+ if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
+ {
+ // Avoid questions with indeterminate answer:
+ // e.g. "0 x ? = 0"
+ if (k == MC_OPER_MULT && i == 0)
+ continue;
+
+ // e.g. "0 / ? = 0"
+ if (k == MC_OPER_DIV && i == 0)
+ {
+ // need to restore i and j to original values so loop works:
+ j = ans;
+ i = tmp;
+ continue;
+ }
+
+ tnode = allocate_node();
+ if(!tnode)
+ {
+ fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
+ delete_list(list);
+ return NULL;
+ }
+
+ snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", j);
+ snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
+ "%d %c ? = %d", i, operchars[k], ans);
+ tnode->card.difficulty = k + 3;
+ list = insert_node(list, *end_of_list, tnode);
+ *end_of_list = tnode;
+ }
+ //If we divided, reset i and j so loop works correctly
+ if (k == MC_OPER_DIV)
+ {
+ j = ans;
+ i = tmp;
+ mcdprintf("resetting to %d %d\n", j, i);
+ }
+ }
+ }
+ }
+ }
+ //add all comparison questions (TODO implement them!)
+ else if (pt == MC_PT_COMPARISON)
+ {
+ for (i = MC_GetOpt(MIN_COMPARATOR); i < MC_GetOpt(MAX_COMPARATOR); ++i)
+ {
+ for (j = MC_GetOpt(MIN_COMPARISAND); j < MC_GetOpt(MAX_COMPARISAND); ++j)
+ {
+ tnode = allocate_node();
+ if(!tnode)
+ {
+ fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
+ delete_list(list);
+ return NULL;
+ }
+
+ snprintf(tnode->card.formula_string, MC_FORMULA_LEN, "%d ? %d", i,j);
+ snprintf(tnode->card.answer_string, MC_ANSWER_LEN,
+ i < j ? "<" :
+ i > j ? ">" :
+ "=");
+ tnode->card.difficulty = 1;
+ list = insert_node(list, *end_of_list, tnode);
+ *end_of_list = tnode;
+ }
+ }
+ }
+ mcdprintf("Exiting add_all_valid()\n");
+ mcdprintf("List now has %d questions\n\n", list_length(list));
+
+ return list;
+}
+
+MC_MathQuestion* find_node(MC_MathQuestion* list, int num)
+{
+ while (--num > 0 && list)
+ list = list->next;
+ return list;
+}
+
+void reformat_arithmetic(MC_FlashCard* card, MC_Format f)
+{
+ int i, j;
+ char* beg = 0;
+ char* end = 0;
+ char nans[MC_ANSWER_LEN];
+ char nformula[MC_FORMULA_LEN + MC_ANSWER_LEN]; //gets a bit larger than usual in the meantime
+
+ {
+ //snprintf(nans, max_answer_size, "%s", card->answer_string);
+
+ //insert old answer where question mark was
+ for (i = 0, j = 0; card->formula_string[j] != '?'; ++i, ++j)
+ nformula[i] = card->formula_string[j];
+ i += snprintf(nformula + i, MC_ANSWER_LEN - 1, "%s", card->answer_string);
+ snprintf(nformula + i, MC_FORMULA_LEN - i, "%s", card->formula_string + j + 1);
+
+ //replace the new answer with a question mark
+ if (f == MC_FORMAT_ANS_LAST)
+ beg = strrchr(nformula, ' ') + 1;
+ if (f == MC_FORMAT_ANS_FIRST)
+ beg = nformula;
+ if (f == MC_FORMAT_ANS_MIDDLE)
+ beg = strchr(nformula, ' ') + 3;
+ end = strchr(beg + 1, ' ');
+ if (!end)
+ end = "";
+ //we now have beg = first digit of number to replace, end = the char after
+ sscanf(beg, "%s", nans);
+ *beg = 0; //sequester the first half of the string
+ snprintf(card->formula_string, MC_FORMULA_LEN, "%s?%s", nformula, end);
+ snprintf(card->answer_string, MC_ANSWER_LEN, "%s", nans);
+ card->answer = atoi(card->answer_string);
+ }
+}
Deleted: tuxmath/branches/lan/src/mathcards.h
===================================================================
--- tuxmath/branches/lan/src/mathcards.h 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/src/mathcards.h 2009-07-30 02:27:03 UTC (rev 1293)
@@ -1,278 +0,0 @@
-/*
-
- mathcards.h
-
- Description: contains headers for a flashcard-type math game.
- This is a sort of interface-independent backend that could be used with a different
- user interface. Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
- (aka tuxmath). If tuxmath were a C++ program, this would be a C++ class.
-
- Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2006
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-#ifndef MATHCARDS_H
-#define MATHCARDS_H
-
-#include "transtruct.h"
-//#define MC_DEBUG
-#ifdef MC_DEBUG
-#define mcdprintf(...) printf(__VA_ARGS__)
-#else
-#define mcdprintf(...) 0
-#endif
-
-
-/* different classes of problems TuxMath will ask */
-typedef enum _MC_ProblemType {
- MC_PT_TYPING,
- MC_PT_ARITHMETIC,
- MC_PT_COMPARISON,
- MC_NUM_PTYPES
-} MC_ProblemType;
-
-/* type of math operation used in an arithmetic question */
-typedef enum _MC_Operation {
- MC_OPER_ADD,
- MC_OPER_SUB,
- MC_OPER_MULT,
- MC_OPER_DIV,
- MC_NUM_OPERS
-} MC_Operation;
-
-/* math question formats: */
-typedef enum _MC_Format {
- MC_FORMAT_ANS_LAST, /* a + b = ? */
- MC_FORMAT_ANS_FIRST, /* ? + b = c */
- MC_FORMAT_ANS_MIDDLE, /* a + ? = c */
- MC_NUM_FORMATS
-} MC_Format;
-
-
-/*
-Indices for the various integer options. These are NOT the actual values!
-Actual values are accessed as such: options.iopts[PLAY_THROUGH_LIST] = val;
-Creating additional [integral] options is now centralized--it should only
-be necessary to add to this list, the list of text, and the list of
-defaults. (Besides actually using the new options!)
-*/
-enum {
- NOT_VALID_OPTION = -1 ,
- PLAY_THROUGH_LIST = 0 , /* play until all questions answered correctly */
- QUESTION_COPIES , /* # times each question is put in list */
- REPEAT_WRONGS , /* reuse incorrectly answered questions or not */
- COPIES_REPEATED_WRONGS , /* how many copies of an incorrectly answered question to re-insert*/
- ALLOW_NEGATIVES ,
- MAX_ANSWER ,
- MAX_QUESTIONS ,
- MAX_FORMULA_NUMS ,
- MIN_FORMULA_NUMS ,
-
- //NOTE: Do _not_ rearrange the FORMAT values because the functions
- //rely on index arithmetic to iterate through these, and will be
- //broken if the relative position changes!
- FORMAT_ANSWER_LAST , /* question format is: a + b = ? */
- FORMAT_ANSWER_FIRST , /* question format is: ? + b = c */
- FORMAT_ANSWER_MIDDLE , /* question format is: a + ? = c */
- FORMAT_ADD_ANSWER_LAST , /* a + b = ? */
- FORMAT_ADD_ANSWER_FIRST , /* ? + b = c */
- FORMAT_ADD_ANSWER_MIDDLE , /* a + ? = c */
- FORMAT_SUB_ANSWER_LAST , /* a - b = ? */
- FORMAT_SUB_ANSWER_FIRST , /* ? - b = c */
- FORMAT_SUB_ANSWER_MIDDLE , /* a - ? = c */
- FORMAT_MULT_ANSWER_LAST , /* a * b = ? */
- FORMAT_MULT_ANSWER_FIRST , /* ? * b = c */
- FORMAT_MULT_ANSWER_MIDDLE , /* a * ? = c */
- FORMAT_DIV_ANSWER_LAST , /* a / b = ? */
- FORMAT_DIV_ANSWER_FIRST , /* ? / b = c */
- FORMAT_DIV_ANSWER_MIDDLE , /* a / ? = c */
-
- ADDITION_ALLOWED ,
- SUBTRACTION_ALLOWED ,
- MULTIPLICATION_ALLOWED ,
- DIVISION_ALLOWED ,
- TYPING_PRACTICE_ALLOWED ,
- ARITHMETIC_ALLOWED ,
- COMPARISON_ALLOWED ,
-
- MIN_AUGEND , /* augend + addend = sum */
- MAX_AUGEND ,
- MIN_ADDEND ,
- MAX_ADDEND ,
-
- MIN_MINUEND , /* minuend - subtrahend = difference */
- MAX_MINUEND ,
- MIN_SUBTRAHEND ,
- MAX_SUBTRAHEND ,
-
- MIN_MULTIPLIER , /* multiplier * multiplicand = product */
- MAX_MULTIPLIER ,
- MIN_MULTIPLICAND ,
- MAX_MULTIPLICAND ,
-
- MIN_DIVISOR , /* dividend/divisor = quotient */
- MAX_DIVISOR , /* note - generate_list() will prevent */
- MIN_QUOTIENT , /* questions with division by zero. */
- MAX_QUOTIENT ,
-
- MIN_TYPING_NUM , /* range for "typing tutor" mode, for */
- MAX_TYPING_NUM , /* kids just learning to use keyboard. */
-
- MIN_COMPARATOR , /* left comparison operand */
- MAX_COMPARATOR ,
- MIN_COMPARISAND , /* right comparison operannd */
- MAX_COMPARISAND ,
-
- RANDOMIZE , /* whether to shuffle cards */
-
- COMPREHENSIVE , /* whether to generate all questions 'in order' */
- AVG_LIST_LENGTH , /* the average number of questions in a list */
- VARY_LIST_LENGTH , /* whether to randomly adjust list length */
-
- NOPTS
-};
-
-extern const char* const MC_OPTION_TEXT[];
-extern const int MC_DEFAULTS[];
-extern const char operchars[MC_NUM_OPERS];
-
-/* default values for math_options */
-#define MC_MAX_DIGITS 3
-#define MC_GLOBAL_MAX 999 /* This is the largest absolute value that */
- /* can be entered for math question values.*/
-#define MC_MATH_OPTS_INVALID -9999 /* Return value for accessor functions */
- /* if math_opts not valid */
-//#define DEFAULT_FRACTION_TO_KEEP 1
-
-
-typedef struct _MC_Options
-{
- int iopts[NOPTS];
-} MC_Options;
-
-
-
-/* struct for node in math "flashcard" list */
-typedef struct MC_MathQuestion {
- MC_FlashCard card;
- struct MC_MathQuestion* next;
- struct MC_MathQuestion* previous;
- int randomizer;
-} MC_MathQuestion;
-
-
-/* "public" function prototypes: these functions are how */
-/* a user interface communicates with MathCards: */
-/* TODO provide comments thoroughly explaining these functions */
-
-/* MC_Initialize() sets up the struct containing all of */
-/* settings regarding math questions. It should be */
-/* called before any other function. Many of the other */
-/* functions will not work properly if MC_Initialize() */
-/* has not been called. It only needs to be called once, */
-/* i.e when the program is starting, not at the beginning*/
-/* of each math game for the player. Returns 1 if */
-/* successful, 0 otherwise. */
-int MC_Initialize(void);
-
-/* MC_StartGame() generates the list of math questions */
-/* based on existing settings. It should be called at */
-/* the beginning of each math game for the player. */
-/* Returns 1 if resultant list contains 1 or more */
-/* questions, 0 if list empty or not generated */
-/* successfully. */
-int MC_StartGame(void);
-
-/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
-/* but uses the incorrectly answered questions from the */
-/* previous game for the question list as a review form */
-/* of learning. If there were no wrong answers (or no */
-/* previous game), it behaves just like MC_StartGame(). */
-/* FIXME wonder if it should generate a message if the */
-/* list is created from settings because there is no */
-/* valid wrong question list? */
-int MC_StartGameUsingWrongs(void);
-
-/* MC_NextQuestion() takes a pointer to an allocated */
-/* MC_MathQuestion struct and fills in the fields for */
-/* use by the user interface program. It basically is */
-/* like taking the next flashcard from the pile. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-/* or if argument pointer is invalid */
-int MC_NextQuestion(MC_FlashCard* q);
-
-/* MC_AnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has been answered */
-/* correctly. Returns 1 if no errors. */
-int MC_AnsweredCorrectly(MC_FlashCard* q);
-int MC_AnsweredCorrectly_id(int id);
-
-/* MC_NotAnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has not been */
-/* answered correctly. Returns 1 if no errors. */
-int MC_NotAnsweredCorrectly(MC_FlashCard* q);
-
-/* Like MC_NextQuestion(), but takes "flashcard" from */
-/* pile of incorrectly answered questions. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-int MC_NextWrongQuest(MC_FlashCard* q);
-
-/* Returns 1 if all have been answered correctly, */
-/* 0 otherwise. */
-int MC_MissionAccomplished(void);
-
-/* Returns number of questions left (either in list */
-/* or "in play") */
-int MC_TotalQuestionsLeft(void);
-
-/* Returns questions left in list, NOT */
-/* including questions currently "in play". */
-int MC_ListQuestionsLeft(void);
-
-/* To keep track of how long students take to answer the */
-/* questions, one can report the time needed to answer */
-/* an individual question: */
-int MC_AddTimeToList(float t);
-/* Note that initialization of the list is handled by */
-/* MC_StartGame. */
-
-/* Tells MathCards to clean up - should be called when */
-/* user interface program exits. */
-void MC_EndGame(void);
-
-/* Prints contents of math_opts struct in human-readable */
-/* form to given file. "verbose" tells the function to */
-/* write a lot of descriptive "help"-type info for each */
-/* option (intended to make config files self-documenting).*/
-void MC_PrintMathOptions(FILE* fp, int verbose);
-
-/* Additional functions used to generate game summary files: */
-int MC_PrintQuestionList(FILE* fp);
-int MC_PrintWrongList(FILE* fp);
-int MC_StartingListLength(void);
-int MC_WrongListLength(void);
-int MC_NumAnsweredCorrectly(void);
-int MC_NumNotAnsweredCorrectly(void);
-float MC_MedianTimePerQuestion(void);
-
-/********************************************
-Public functions for new mathcards architecture
-*********************************************/
-/* Return the array index of the given text, e.g. randomize->47 */
-unsigned int MC_MapTextToIndex(const char* text);
-void MC_SetOpt(unsigned int index, int val); //access directly,for internal use
-int MC_GetOpt(unsigned int index);
-void MC_SetOp(const char* param, int val); //access by text, for config reading
-int MC_GetOp(const char* param);
-int MC_VerifyOptionListSane(void);
-int MC_MaxFormulaSize(void); //amount of memory needed to safely hold strings
-int MC_MaxAnswerSize(void);
-MC_FlashCard MC_AllocateFlashcard();
-void MC_FreeFlashcard(MC_FlashCard* fc);
-void MC_ResetFlashCard(MC_FlashCard* fc); //empty flashcard of strings & values
-int MC_FlashCardGood(const MC_FlashCard* fc); //verifies a flashcard is valid
-/* Reorganize formula_string and answer_string to render the same equation
- in a different format */
-void reformat_arithmetic(MC_FlashCard* card, MC_Format f);
-#endif
Copied: tuxmath/branches/lan/src/mathcards.h (from rev 1292, tuxmath/branches/lan/server/mathcards.h)
===================================================================
--- tuxmath/branches/lan/src/mathcards.h (rev 0)
+++ tuxmath/branches/lan/src/mathcards.h 2009-07-30 02:27:03 UTC (rev 1293)
@@ -0,0 +1,280 @@
+/*
+
+ mathcards.h
+
+ Description: contains headers for a flashcard-type math game.
+ This is a sort of interface-independent backend that could be used with a different
+ user interface. Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
+ (aka tuxmath). If tuxmath were a C++ program, this would be a C++ class.
+
+ Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2006
+
+ Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
+
+*/
+#ifndef MATHCARDS_H
+#define MATHCARDS_H
+
+#include "transtruct.h"
+
+//#define MC_DEBUG
+#ifdef MC_DEBUG
+#define mcdprintf(...) printf(__VA_ARGS__)
+#else
+#define mcdprintf(...) 0
+#endif
+
+
+/* different classes of problems TuxMath will ask */
+typedef enum _MC_ProblemType {
+ MC_PT_TYPING,
+ MC_PT_ARITHMETIC,
+ MC_PT_COMPARISON,
+ MC_NUM_PTYPES
+} MC_ProblemType;
+
+/* type of math operation used in an arithmetic question */
+typedef enum _MC_Operation {
+ MC_OPER_ADD,
+ MC_OPER_SUB,
+ MC_OPER_MULT,
+ MC_OPER_DIV,
+ MC_NUM_OPERS
+} MC_Operation;
+
+/* math question formats: */
+typedef enum _MC_Format {
+ MC_FORMAT_ANS_LAST, /* a + b = ? */
+ MC_FORMAT_ANS_FIRST, /* ? + b = c */
+ MC_FORMAT_ANS_MIDDLE, /* a + ? = c */
+ MC_NUM_FORMATS
+} MC_Format;
+
+
+/*
+Indices for the various integer options. These are NOT the actual values!
+Actual values are accessed as such: options.iopts[PLAY_THROUGH_LIST] = val;
+Creating additional [integral] options is now centralized--it should only
+be necessary to add to this list, the list of text, and the list of
+defaults. (Besides actually using the new options!)
+*/
+enum {
+ NOT_VALID_OPTION = -1 ,
+ PLAY_THROUGH_LIST = 0 , /* play until all questions answered correctly */
+ QUESTION_COPIES , /* # times each question is put in list */
+ REPEAT_WRONGS , /* reuse incorrectly answered questions or not */
+ COPIES_REPEATED_WRONGS , /* how many copies of an incorrectly answered question to re-insert*/
+ ALLOW_NEGATIVES ,
+ MAX_ANSWER ,
+ MAX_QUESTIONS ,
+ MAX_FORMULA_NUMS ,
+ MIN_FORMULA_NUMS ,
+
+ //NOTE: Do _not_ rearrange the FORMAT values because the functions
+ //rely on index arithmetic to iterate through these, and will be
+ //broken if the relative position changes!
+ FORMAT_ANSWER_LAST , /* question format is: a + b = ? */
+ FORMAT_ANSWER_FIRST , /* question format is: ? + b = c */
+ FORMAT_ANSWER_MIDDLE , /* question format is: a + ? = c */
+ FORMAT_ADD_ANSWER_LAST , /* a + b = ? */
+ FORMAT_ADD_ANSWER_FIRST , /* ? + b = c */
+ FORMAT_ADD_ANSWER_MIDDLE , /* a + ? = c */
+ FORMAT_SUB_ANSWER_LAST , /* a - b = ? */
+ FORMAT_SUB_ANSWER_FIRST , /* ? - b = c */
+ FORMAT_SUB_ANSWER_MIDDLE , /* a - ? = c */
+ FORMAT_MULT_ANSWER_LAST , /* a * b = ? */
+ FORMAT_MULT_ANSWER_FIRST , /* ? * b = c */
+ FORMAT_MULT_ANSWER_MIDDLE , /* a * ? = c */
+ FORMAT_DIV_ANSWER_LAST , /* a / b = ? */
+ FORMAT_DIV_ANSWER_FIRST , /* ? / b = c */
+ FORMAT_DIV_ANSWER_MIDDLE , /* a / ? = c */
+
+ ADDITION_ALLOWED ,
+ SUBTRACTION_ALLOWED ,
+ MULTIPLICATION_ALLOWED ,
+ DIVISION_ALLOWED ,
+ TYPING_PRACTICE_ALLOWED ,
+ ARITHMETIC_ALLOWED ,
+ COMPARISON_ALLOWED ,
+
+ MIN_AUGEND , /* augend + addend = sum */
+ MAX_AUGEND ,
+ MIN_ADDEND ,
+ MAX_ADDEND ,
+
+ MIN_MINUEND , /* minuend - subtrahend = difference */
+ MAX_MINUEND ,
+ MIN_SUBTRAHEND ,
+ MAX_SUBTRAHEND ,
+
+ MIN_MULTIPLIER , /* multiplier * multiplicand = product */
+ MAX_MULTIPLIER ,
+ MIN_MULTIPLICAND ,
+ MAX_MULTIPLICAND ,
+
+ MIN_DIVISOR , /* dividend/divisor = quotient */
+ MAX_DIVISOR , /* note - generate_list() will prevent */
+ MIN_QUOTIENT , /* questions with division by zero. */
+ MAX_QUOTIENT ,
+
+ MIN_TYPING_NUM , /* range for "typing tutor" mode, for */
+ MAX_TYPING_NUM , /* kids just learning to use keyboard. */
+
+ MIN_COMPARATOR , /* left comparison operand */
+ MAX_COMPARATOR ,
+ MIN_COMPARISAND , /* right comparison operannd */
+ MAX_COMPARISAND ,
+
+ RANDOMIZE , /* whether to shuffle cards */
+
+ COMPREHENSIVE , /* whether to generate all questions 'in order' */
+ AVG_LIST_LENGTH , /* the average number of questions in a list */
+ VARY_LIST_LENGTH , /* whether to randomly adjust list length */
+
+ NOPTS
+};
+
+extern const char* const MC_OPTION_TEXT[];
+extern const int MC_DEFAULTS[];
+extern const char operchars[MC_NUM_OPERS];
+
+/* default values for math_options */
+#define MC_MAX_DIGITS 3
+#define MC_GLOBAL_MAX 999 /* This is the largest absolute value that */
+ /* can be entered for math question values.*/
+#define MC_MATH_OPTS_INVALID -9999 /* Return value for accessor functions */
+ /* if math_opts not valid */
+//#define DEFAULT_FRACTION_TO_KEEP 1
+
+
+typedef struct _MC_Options
+{
+ int iopts[NOPTS];
+} MC_Options;
+
+
+
+/* struct for node in math "flashcard" list */
+typedef struct MC_MathQuestion {
+ MC_FlashCard card;
+ struct MC_MathQuestion* next;
+ struct MC_MathQuestion* previous;
+ int randomizer;
+} MC_MathQuestion;
+
+
+/* "public" function prototypes: these functions are how */
+/* a user interface communicates with MathCards: */
+/* TODO provide comments thoroughly explaining these functions */
+
+/* MC_Initialize() sets up the struct containing all of */
+/* settings regarding math questions. It should be */
+/* called before any other function. Many of the other */
+/* functions will not work properly if MC_Initialize() */
+/* has not been called. It only needs to be called once, */
+/* i.e when the program is starting, not at the beginning*/
+/* of each math game for the player. Returns 1 if */
+/* successful, 0 otherwise. */
+int MC_Initialize(void);
+
+/* MC_StartGame() generates the list of math questions */
+/* based on existing settings. It should be called at */
+/* the beginning of each math game for the player. */
+/* Returns 1 if resultant list contains 1 or more */
+/* questions, 0 if list empty or not generated */
+/* successfully. */
+int MC_StartGame(void);
+
+/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
+/* but uses the incorrectly answered questions from the */
+/* previous game for the question list as a review form */
+/* of learning. If there were no wrong answers (or no */
+/* previous game), it behaves just like MC_StartGame(). */
+/* FIXME wonder if it should generate a message if the */
+/* list is created from settings because there is no */
+/* valid wrong question list? */
+int MC_StartGameUsingWrongs(void);
+
+/* MC_NextQuestion() takes a pointer to an allocated */
+/* MC_MathQuestion struct and fills in the fields for */
+/* use by the user interface program. It basically is */
+/* like taking the next flashcard from the pile. */
+/* Returns 1 if question found, 0 if list empty/invalid */
+/* or if argument pointer is invalid */
+int MC_NextQuestion(MC_FlashCard* q);
+
+/* MC_AnsweredCorrectly() is how the user interface */
+/* tells MathCards that the question has been answered */
+/* correctly. Returns 1 if no errors. */
+int MC_AnsweredCorrectly(MC_FlashCard* q);
+int MC_AnsweredCorrectly_id(int id);
+
+/* MC_NotAnsweredCorrectly() is how the user interface */
+/* tells MathCards that the question has not been */
+/* answered correctly. Returns 1 if no errors. */
+int MC_NotAnsweredCorrectly(MC_FlashCard* q);
+int MC_NotAnsweredCorrectly_id(int id);
+
+/* Like MC_NextQuestion(), but takes "flashcard" from */
+/* pile of incorrectly answered questions. */
+/* Returns 1 if question found, 0 if list empty/invalid */
+int MC_NextWrongQuest(MC_FlashCard* q);
+
+/* Returns 1 if all have been answered correctly, */
+/* 0 otherwise. */
+int MC_MissionAccomplished(void);
+
+/* Returns number of questions left (either in list */
+/* or "in play") */
+int MC_TotalQuestionsLeft(void);
+
+/* Returns questions left in list, NOT */
+/* including questions currently "in play". */
+int MC_ListQuestionsLeft(void);
+
+/* To keep track of how long students take to answer the */
+/* questions, one can report the time needed to answer */
+/* an individual question: */
+int MC_AddTimeToList(float t);
+/* Note that initialization of the list is handled by */
+/* MC_StartGame. */
+
+/* Tells MathCards to clean up - should be called when */
+/* user interface program exits. */
+void MC_EndGame(void);
+
+/* Prints contents of math_opts struct in human-readable */
+/* form to given file. "verbose" tells the function to */
+/* write a lot of descriptive "help"-type info for each */
+/* option (intended to make config files self-documenting).*/
+void MC_PrintMathOptions(FILE* fp, int verbose);
+
+/* Additional functions used to generate game summary files: */
+int MC_PrintQuestionList(FILE* fp);
+int MC_PrintWrongList(FILE* fp);
+int MC_StartingListLength(void);
+int MC_WrongListLength(void);
+int MC_NumAnsweredCorrectly(void);
+int MC_NumNotAnsweredCorrectly(void);
+float MC_MedianTimePerQuestion(void);
+
+/********************************************
+Public functions for new mathcards architecture
+*********************************************/
+/* Return the array index of the given text, e.g. randomize->47 */
+unsigned int MC_MapTextToIndex(const char* text);
+void MC_SetOpt(unsigned int index, int val); //access directly,for internal use
+int MC_GetOpt(unsigned int index);
+void MC_SetOp(const char* param, int val); //access by text, for config reading
+int MC_GetOp(const char* param);
+int MC_VerifyOptionListSane(void);
+int MC_MaxFormulaSize(void); //amount of memory needed to safely hold strings
+int MC_MaxAnswerSize(void);
+MC_FlashCard MC_AllocateFlashcard();
+void MC_FreeFlashcard(MC_FlashCard* fc);
+void MC_ResetFlashCard(MC_FlashCard* fc); //empty flashcard of strings & values
+int MC_FlashCardGood(const MC_FlashCard* fc); //verifies a flashcard is valid
+/* Reorganize formula_string and answer_string to render the same equation
+ in a different format */
+void reformat_arithmetic(MC_FlashCard* card, MC_Format f);
+#endif
Modified: tuxmath/branches/lan/src/network.c
===================================================================
--- tuxmath/branches/lan/src/network.c 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/src/network.c 2009-07-30 02:27:03 UTC (rev 1293)
@@ -251,17 +251,17 @@
-int LAN_NextQuestion(void)
-{
- char buf[NET_BUF_LEN];
+// int LAN_NextQuestion(void)
+// {
+// char buf[NET_BUF_LEN];
+//
+// snprintf(buf, NET_BUF_LEN,
+// "%s",
+// "NEXT_QUESTION");
+// return say_to_server(buf);
+// }
- snprintf(buf, NET_BUF_LEN,
- "%s",
- "NEXT_QUESTION");
- return say_to_server(buf);
-}
-
/* Appears a return value of 0 means message received, 1 means no socket activity */
int check_messages(char buf[NET_BUF_LEN])
{
Modified: tuxmath/branches/lan/src/network.h
===================================================================
--- tuxmath/branches/lan/src/network.h 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/src/network.h 2009-07-30 02:27:03 UTC (rev 1293)
@@ -38,18 +38,17 @@
/* Network replacement functions for mathcards "API": */
/* These functions are how the client tells things to the server: */
int LAN_StartGame(void);
-/* NOTE probably won't have this in multiplayer - new quests determined by server */
-int LAN_NextQuestion(void);
int LAN_AnsweredCorrectly(MC_FlashCard* fc);
int LAN_NotAnsweredCorrectly(MC_FlashCard* fc);
int LAN_LeaveGame(void);
/* This is how the client receives messages from the server: */
int LAN_NextMsg(char* buf);
+/* NOTE probably won't have this in multiplayer - new quests determined by server */
+//int LAN_NextQuestion(void);
-
/* FIXME appears this one is basically the same as LAN_NextMsg() */
int check_messages(char *);
/* FIXME this should be local to network.c */
Modified: tuxmath/branches/lan/src/transtruct.h
===================================================================
--- tuxmath/branches/lan/src/transtruct.h 2009-07-29 18:34:18 UTC (rev 1292)
+++ tuxmath/branches/lan/src/transtruct.h 2009-07-30 02:27:03 UTC (rev 1293)
@@ -26,19 +26,7 @@
#define TEST_COMETS 10
-#ifndef MC_USE_NEWARC
-/* struct for individual "flashcard" */
-typedef struct MC_FlashCard {
- int num1;
- int num2;
- int num3;
- int operation;
- int format;
- char formula_string[MC_FORMULA_LEN];
- char answer_string[MC_ANSWER_LEN];
-} MC_FlashCard;
-#else
-/* experimental struct for a more generalized flashcard */
+
typedef struct _MC_FlashCard {
char formula_string[MC_FORMULA_LEN];
char answer_string[MC_ANSWER_LEN];
@@ -46,7 +34,6 @@
int answer;
int difficulty;
} MC_FlashCard;
-#endif
#endif
More information about the Tux4kids-commits
mailing list