[Tux4kids-commits] r10 - in tuxmath/trunk: . docs src
dbruce at alioth.debian.org
dbruce at alioth.debian.org
Thu Mar 8 20:58:32 CET 2007
Author: dbruce
Date: 2006-05-16 17:05:20 +0000 (Tue, 16 May 2006)
New Revision: 10
Added:
tuxmath/trunk/src/mathcards.c
tuxmath/trunk/src/mathcards.h
Modified:
tuxmath/trunk/Makefile
tuxmath/trunk/docs/CHANGES.txt
tuxmath/trunk/src/game.c
tuxmath/trunk/src/game.h
tuxmath/trunk/src/options.c
tuxmath/trunk/src/setup.c
tuxmath/trunk/src/tuxmath.c
tuxmath/trunk/src/tuxmath.h
Log:
Incorporated MathCards backend.
Modified: tuxmath/trunk/Makefile
===================================================================
--- tuxmath/trunk/Makefile 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/Makefile 2006-05-16 17:05:20 UTC (rev 10)
@@ -61,7 +61,8 @@
tuxmath: obj/tuxmath.o obj/setup.o obj/title.o obj/game.o \
- obj/options.o obj/credits.o obj/playsound.o
+ obj/options.o obj/credits.o obj/playsound.o \
+ obj/mathcards.o
@echo "LINKING!"
$(CC) $(CFLAGS) $^ -o tuxmath $(LIBS)
@@ -111,3 +112,7 @@
-mkdir -p obj
$(CC) $(CFLAGS) src/playsound.c -c -o obj/playsound.o
+obj/mathcards.o: src/mathcards.c src/mathcards.h
+ @echo "BUILDING mathcards.o"
+ -mkdir -p obj
+ $(CC) $(CFLAGS) src/mathcards.c -c -o obj/mathcards.o
\ No newline at end of file
Modified: tuxmath/trunk/docs/CHANGES.txt
===================================================================
--- tuxmath/trunk/docs/CHANGES.txt 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/docs/CHANGES.txt 2006-05-16 17:05:20 UTC (rev 10)
@@ -1,5 +1,31 @@
CHANGES.txt for "tuxmath"
+2006.May.16 (https://svn.tux4kids.net/tuxmath/ - revision 8)
+ Code:
+ * Major changes to internal workings of program. Everything
+ related to generation of math questions is now handled by
+ a backend called MathCards, which generates question lists
+ based on parameters in a struct called math_opts. MathCards
+ is (obviously) contained in two new files, mathcards.h and
+ mathcards.c. For now, many options can only be set at
+ compile time by changing the defaults in mathcards.h.
+ MathCards allows fine-grained control of the questions to
+ be asked, and allows the player to "win" if all of the
+ questions in the list are answered while the cities are
+ still alive. By default, game behavior is unchanged from
+ previously.
+ * Main game() function in game.c has been split into several
+ smaller functions; updated to use MathCards.
+ * Options() updated to use MathCards.
+ Game:
+ * demo mode now handles negative answers properly.
+
+ Note: the code contains many FIXMEs and TODOs and should be
+ tested more before being packaged for a distribution.
+ I would describe revision 8 as a developer or alpha release.
+
+ David Bruce <dbruce at tampabay.rr.com>
+
2006.Mar.8 (https://svn.tux4kids.net/tuxmath/ - revision 7)
Setup:
* updated usage() to include all command-line options
Modified: tuxmath/trunk/src/game.c
===================================================================
--- tuxmath/trunk/src/game.c 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/game.c 2006-05-16 17:05:20 UTC (rev 10)
@@ -30,12 +30,13 @@
#include "sounds.h"
#include "playsound.h"
#include "tuxmath.h"
+#include "mathcards.h"
#define FPS (1000 / 15) /* 15 fps max */
#define CITY_EXPL_START 3 * 5 /* Must be mult. of 5 (number of expl frames) */
#define ANIM_FRAME_START 4 * 2 /* Must be mult. of 2 (number of tux frames) */
-#define GAMEOVER_COUNTER_START 75
+#define GAMEOVER_COUNTER_START 750
#define LEVEL_START_WAIT_START 20
#define LASER_START 5
@@ -51,17 +52,13 @@
"addition", "subtraction", "multiplication", "division"
};
-typedef struct range_type {
- int min;
- int max;
-} range_type;
-
-static range_type ranges[NUM_Q_RANGES] = {
+range_type ranges[NUM_Q_RANGES] = {
{0, 5},
{6, 12},
{13, 20}
};
+
#define ANSWER_LEN 5
#define FORMULA_LEN 8
typedef struct comet_type {
@@ -73,71 +70,282 @@
char formula[FORMULA_LEN];
int answer;
char answer_str[ANSWER_LEN];
+ MC_FlashCard flashcard;
} comet_type;
/* Local (to game.c) 'globals': */
-static int wave, score, pre_wave_score, num_attackers;
+static int quit;
+static int done;
+static int gameover;
+static int game_status;
+static int SDL_quit_received;
+static int escape_received;
+static int paused;
+static int wave;
+static int score;
+static int pre_wave_score;
+static int num_attackers;
+static int demo_countdown;
+static int tux_anim;
+static int tux_anim_frame;
+static int num_cities_alive;
+static int num_comets_alive;
+static int tux_img;
+static int old_tux_img;
+static int frame;
+static int neg_answer_picked;
+static int tux_pressing;
+static int doing_answer;
+static int level_start_wait;
+static int last_bkgd;
+
+static Uint32 last_time, now_time;
+
static int digits[3];
static comet_type comets[MAX_COMETS];
static city_type cities[NUM_CITIES];
static laser_type laser;
-static SDL_Surface * bkgd;
-static int last_bkgd;
+static SDL_Surface* bkgd;
-static int neg_answer_picked;
-static int tux_pressing, done, paused, doing_answer;
-static int level_start_wait;
-
/* Local function prototypes: */
+static int game_initialize(void);
+static void game_handle_user_events(void);
+static void game_handle_demo(void);
+static void game_handle_answer(void);
+static void game_countdown(void);
+static void game_handle_tux(void);
+static void game_handle_comets(void);
+static void game_handle_cities(void);
+static void game_draw(void);
+static int check_exit_conditions(void);
+static void print_exit_conditions(void);
static void reset_level(void);
-static void add_comet(void);
-static void draw_numbers(char * str, int x);
-static int pause_game(void);
+static int add_comet(void);
+static void draw_numbers(char* str, int x);
+static int pause_game(void);
static void draw_line(int x1, int y1, int x2, int y2, int r, int g, int b);
static void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel);
static void draw_console_image(int i);
static void add_score(int inc);
-static int pick_operand(int min);
-static int in_range(int n);
+static int pick_operand(int min);
+static int in_range(int n);
+static void reset_comets(void);
static void draw_led_nums(void);
-static void game_mouse_event(SDL_Event the_event);
-static void game_key_event(SDLKey pressed_key);
+static void game_mouse_event(SDL_Event event);
+static void game_key_event(SDLKey key);
+
/* --- MAIN GAME FUNCTION!!! --- */
-/* FIXME consider breaking this large function up into more managable pieces */
-/* e.g. initialize(), handle_events(), update_data(), draw(), etc */
+
int game(void)
{
- int i, j, num, img, quit, frame, lowest, lowest_y,
- tux_img, old_tux_img, tux_anim, tux_anim_frame,
- tux_same_counter, num_cities_alive,
- num_comets_alive, demo_countdown, picked_comet, answer_digit,
- gameover;
- SDL_Event event;
- Uint32 last_time, now_time;
- SDLKey key;
- SDL_Rect src, dest;
- char str[64];
- char* comet_str;
+ /* most code moved into smaller functions (game_*()): */
+ if (!game_initialize())
+ {
+ printf("\ngame_initialize() failed!");
+ fprintf(stderr, "\ngame_initialze() failed!");
+ done = 1;
+ return 0;
+ /*exit(1);*/
+ }
+
+ /* --- MAIN GAME LOOP: --- */
+ do
+ {
+ /* reset or increment various things with each loop: */
+ frame++;
+ last_time = SDL_GetTicks();
+ old_tux_img = tux_img;
+ tux_pressing = 0;
+
+ game_handle_user_events();
+
+ if (doing_answer)
+ {
+ game_handle_answer();
+ }
+
+ if (laser.alive > 0)
+ {
+ laser.alive--;
+ }
+
+ if (level_start_wait > 0)
+ {
+ game_countdown();
+ }
+
+ game_handle_tux();
+ game_handle_comets();
+ game_handle_cities();
+
+
+ if (game_options->demo_mode)
+ {
+ game_handle_demo();
+ }
+ /* drawing code moved into own function: */
+ game_draw();
+
+
+ /* If we're in "PAUSE" mode, pause! */
+ if (paused)
+ {
+ /*quit = */pause_game();
+ paused = 0;
+ }
+
+ /* Keep playing music: */
+
+#ifndef NOSOUND
+ if (game_options->use_sound)
+ {
+ if (!Mix_PlayingMusic())
+ {
+ Mix_PlayMusic(musics[MUS_GAME + (rand() % 3)], 0);
+ }
+ }
+#endif
+ /* figure out if we should leave loop: */
+ game_status = check_exit_conditions();
+
+ /* Pause (keep frame-rate event) */
+ now_time = SDL_GetTicks();
+ if (now_time < last_time + FPS)
+ {
+ SDL_Delay(last_time + FPS - now_time);
+ }
+ }
+ while(GAME_IN_PROGRESS == game_status);
+ /* END OF MAIN GAME LOOP! */
+
+ #ifdef TUXMATH_DEBUG
+ print_exit_conditions();
+ #endif
+
+ /* TODO: handle various end of game scenarios based on value of game_status */
+ switch (game_status)
+ {
+ SDL_Rect dest;
+ SDL_Event event;
+
+ case GAME_OVER_WON:
+ {
+ break;
+ }
+
+ case GAME_OVER_ERROR:
+ {
+ #ifdef TUXMATH_DEBUG
+ printf("\ngame() exiting with error");
+ #endif
+ }
+ case GAME_OVER_LOST:
+ case GAME_OVER_OTHER:
+ {
+ gameover = 1;
+ do
+ {
+
+ while (SDL_PollEvent(&event) > 0)
+ {
+ if (event.type == SDL_QUIT
+ || event.type == SDL_KEYDOWN
+ || event.type == SDL_MOUSEBUTTONDOWN)
+ {
+ gameover = 0;
+ }
+ }
+
+ dest.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
+ dest.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
+ dest.w = images[IMG_GAMEOVER]->w;
+ dest.h = images[IMG_GAMEOVER]->h;
+
+ SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest);
+ SDL_Flip(screen);
+
+ now_time = SDL_GetTicks();
+
+ if (now_time < last_time + FPS)
+ SDL_Delay(last_time + FPS - now_time);
+ }
+ while (gameover);
+
+ break;
+ }
+
+ case GAME_OVER_ESCAPE:
+ {
+ break;
+ }
+
+ case GAME_OVER_WINDOW_CLOSE:
+ {
+ break;
+ }
+
+ }
+
+ /* Free background: */
+ if (bkgd != NULL)
+ {
+ SDL_FreeSurface(bkgd);
+ }
+
+ /* Stop music: */
+#ifndef NOSOUND
+ if (game_options->use_sound)
+ {
+ if (Mix_PlayingMusic())
+ {
+ Mix_HaltMusic();
+ }
+ }
+#endif
+
+ /* Return the chosen command: */
+ return quit;
+}
+
+
+
+int game_initialize(void)
+{
+ int i;
/* Clear window: */
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
SDL_Flip(screen);
-
-
- /* --- MAIN GAME LOOP: --- */
+ /* FIXME done and quit will be superseded by game_status */
done = 0;
quit = 0;
+ game_status = GAME_IN_PROGRESS;
+ SDL_quit_received = 0;
+ escape_received = 0;
+
+ /* Start MathCards backend: */
+ /* FIXME may need to move this into tuxmath.c to accomodate option */
+ /* to us MC_StartUsingWrongs() */
+ if (!MC_StartGame())
+ {
+ printf("\nMC_StartGame() failed!");
+ fprintf(stderr, "\nMC_StartGame() failed!");
+ done = 1;
+ return 0;
+ /*exit(1);*/
+ }
/* Prepare to start the game: */
wave = 1;
+ num_attackers = 2;
score = 0;
gameover = 0;
demo_countdown = 1000;
@@ -174,547 +382,484 @@
/* (Clear laser) */
-
laser.alive = 0;
-
/* Reset remaining stuff: */
bkgd = NULL;
last_bkgd = -1;
reset_level();
-
-
- /* --- MAIN GAME LOOP!!! --- */
-
+ reset_comets();
+
frame = 0;
paused = 0;
- picked_comet = -1;
- answer_digit = 0;
doing_answer = 0;
+ tux_pressing = 0;
tux_img = IMG_TUX_RELAX1;
tux_anim = -1;
tux_anim_frame = 0;
- tux_same_counter = 0;
-
- do
- {
- frame++;
- last_time = SDL_GetTicks();
+ return 1;
+}
+void game_handle_user_events(void)
+{
+ SDL_Event event;
+ SDLKey key;
- /* Handle any incoming events: */
-
- old_tux_img = tux_img;
- tux_pressing = 0;
+ while (SDL_PollEvent(&event) > 0)
+ {
+ if (event.type == SDL_QUIT)
+ {
+ SDL_quit_received = 1;
+ /* FIXME quit and done flags will be superseded */
+ /* Window close event - quit! */
+ quit = 1;
+ done = 1;
+ }
+ else if (event.type == SDL_KEYDOWN)
+ {
+ key = event.key.keysym.sym;
+ game_key_event(key);
+ }
+ else if (event.type == SDL_MOUSEBUTTONDOWN)
+ {
+ game_mouse_event(event);
+ }
+ }
+}
- while (SDL_PollEvent(&event) > 0)
- {
- if (event.type == SDL_QUIT)
- {
- /* Window close event - quit! */
- quit = 1;
- done = 1;
- }
- else if (event.type == SDL_KEYDOWN)
- {
- key = event.key.keysym.sym;
- game_key_event(key);
- }
- else if (event.type == SDL_MOUSEBUTTONDOWN)
- {
- game_mouse_event(event);
- }
- }
+void game_handle_demo(void)
+{
+ /* Demo mode! */
+ int demo_answer, answer_digit;
+ static int picked_comet;
+ if (picked_comet == -1 && (rand() % 10) < 3)
+ {
+ /* Demo mode! Randomly pick a comet to destroy: */
+ picked_comet = (rand() % MAX_COMETS);
+ if (!comets[picked_comet].alive || comets[picked_comet].y < 80)
+ {
+ picked_comet = -1;
+ }
+ else
+ {
+ /* found a comet to blow up! */
+ demo_answer = comets[picked_comet].answer;
+ /* handle negative answer: */
+ if (demo_answer < 0)
+ {
+ demo_answer = -demo_answer;
+ neg_answer_picked = 1;
+ }
+ if (demo_answer >= 100)
+ answer_digit = 0;
+ else if (demo_answer >= 10)
+ answer_digit = 1;
+ else
+ answer_digit = 2;
+ }
+ }
+
+ /* Add a digit: */
+ if (picked_comet != -1 && (frame % 5) == 0 && (rand() % 10) < 8)
+ {
+ tux_pressing = 1;
+ if (answer_digit < 3)
+ {
+ digits[0] = digits[1];
+ digits[1] = digits[2];
- if (game_options->demo_mode)
+ if (answer_digit == 0)
{
- /* Demo mode! */
- /* FIXME update this to handle negatives correctly */
+ digits[2] = demo_answer / 100;
+ }
+ else if (answer_digit == 1)
+ {
+ digits[2] = (demo_answer % 100) / 10;
+ }
+ else if (answer_digit == 2)
+ {
+ digits[2] = (demo_answer % 10);
+ }
+
+ answer_digit++;
+ }
+ else
+ {
+ /* "Press Return" */
+ doing_answer = 1;
+ picked_comet = -1;
+ }
+ }
- if (picked_comet == -1 && (rand() % 10) < 3)
- {
- /* Demo mode! Randomly pick a comet to destroy: */
-
- picked_comet = (rand() % MAX_COMETS);
-
- if (!comets[picked_comet].alive || comets[picked_comet].y < 80)
- picked_comet = -1;
- else
- {
- if (comets[picked_comet].answer >= 100)
- answer_digit = 0;
- else if (comets[picked_comet].answer >= 10)
- answer_digit = 1;
- else
- answer_digit = 2;
- }
- }
-
+ /* Count down counter: */
+ demo_countdown--;
+ if (demo_countdown <= 0 || num_cities_alive == 0)
+ done = 1;
+}
- /* Add a digit: */
+/* FIXME need to end game if no more questions left */
+void game_handle_answer(void)
+{
+ int i, num, lowest, lowest_y;
- if (picked_comet != -1 && (frame % 5) == 0 && (rand() % 10) < 8)
- {
- tux_pressing = 1;
-
- if (answer_digit < 3)
- {
- digits[0] = digits[1];
- digits[1] = digits[2];
+ doing_answer = 0;
- if (answer_digit == 0)
- {
- digits[2] = comets[picked_comet].answer / 100;
- }
- else if (answer_digit == 1)
- {
- digits[2] = (comets[picked_comet].answer % 100) / 10;
- }
- else if (answer_digit == 2)
- {
- digits[2] = (comets[picked_comet].answer % 10);
- }
-
- answer_digit++;
- }
- else
- {
- /* "Press Return" */
-
- doing_answer = 1;
- picked_comet = -1;
- }
- }
-
-
- /* Count down counter: */
+ num = (digits[0] * 100 +
+ digits[1] * 10 +
+ digits[2]);
+ /* negative answer support DSB */
+ if (neg_answer_picked)
+ {
+ num = -num;
+ }
- demo_countdown--;
-
- if (demo_countdown <= 0 || num_cities_alive == 0)
- done = 1;
- }
- /* end of demo mode code */
-
- /* Handle answer: */
-
- if (doing_answer)
- {
- doing_answer = 0;
+ /* Pick the lowest comet which has the right answer: */
+ lowest_y = 0;
+ lowest = -1;
- num = (digits[0] * 100 +
- digits[1] * 10 +
- digits[2]);
- /* negative answer support DSB */
- if (neg_answer_picked)
- {
- num = -num;
- }
+ for (i = 0; i < MAX_COMETS; i++)
+ {
+ if (comets[i].alive &&
+ comets[i].expl < COMET_EXPL_END &&
+ comets[i].answer == num &&
+ comets[i].y > lowest_y)
+ {
+ lowest = i;
+ lowest_y = comets[i].y;
+ }
+ }
- /* Pick the lowest comet which has the right answer: */
-
- lowest_y = 0;
- lowest = -1;
-
- for (i = 0; i < MAX_COMETS; i++)
- {
- if (comets[i].alive &&
- comets[i].expl < COMET_EXPL_END &&
- comets[i].answer == num &&
- comets[i].y > lowest_y)
- {
- lowest = i;
- lowest_y = comets[i].y;
- }
- }
-
-
- /* If there was an comet with this answer, destroy it! */
-
- if (lowest != -1)
- {
- /* Destroy comet: */
-
- comets[lowest].expl = COMET_EXPL_START;
-
+ /* If there was an comet with this answer, destroy it! */
+ if (lowest != -1) /* -1 means no comet had this answer */
+ {
+ MC_AnsweredCorrectly(&(comets[lowest].flashcard));
- /* Fire laser: */
+ /* Destroy comet: */
+ comets[lowest].expl = COMET_EXPL_START;
+ /* Fire laser: */
+ laser.alive = LASER_START;
+ laser.x1 = screen->w / 2;
+ laser.y1 = screen->h;
+ laser.x2 = comets[lowest].x;
+ laser.y2 = comets[lowest].y;
+ playsound(SND_LASER);
+ playsound(SND_SIZZLE);
- laser.alive = LASER_START;
-
- laser.x1 = screen->w / 2;
- laser.y1 = screen->h;
-
- laser.x2 = comets[lowest].x;
- laser.y2 = comets[lowest].y;
-
- playsound(SND_LASER);
- playsound(SND_SIZZLE);
-
- /* 50% of the time.. */
-
- if ((rand() % 10) < 5)
- {
- /* ... pick an animation to play: */
-
- if ((rand() % 10) < 5)
- tux_anim = IMG_TUX_YES1;
- else
- tux_anim = IMG_TUX_YAY1;
-
- tux_anim_frame = ANIM_FRAME_START;
- }
+ /* FIXME maybe should move this into game_handle_tux() */
+ /* 50% of the time.. */
+ if ((rand() % 10) < 5)
+ {
+ /* ... pick an animation to play: */
+ if ((rand() % 10) < 5)
+ tux_anim = IMG_TUX_YES1;
+ else
+ tux_anim = IMG_TUX_YAY1;
+ tux_anim_frame = ANIM_FRAME_START;
+ }
+ /* Increment score: */
- /* Increment score: */
-
- /* [ add = 25, sub = 50, mul = 75, div = 100 ] */
- /* [ the higher the better ] */
-
- add_score(((25 * (comets[lowest].oper + 1)) *
- (screen->h - comets[lowest].y + 1)) /
- screen->h);
- }
- else
- {
- /* Didn't hit anything! */
+ /* [ add = 25, sub = 50, mul = 75, div = 100 ] */
+ /* [ the higher the better ] */
+ add_score(((25 * (comets[lowest].oper + 1)) *
+ (screen->h - comets[lowest].y + 1)) /
+ screen->h);
+ }
+ else
+ {
+ /* Didn't hit anything! */
+ laser.alive = LASER_START;
+ laser.x1 = screen->w / 2;
+ laser.y1 = screen->h;
+ laser.x2 = laser.x1;
+ laser.y2 = 0;
+ playsound(SND_LASER);
+ playsound(SND_BUZZ);
- laser.alive = LASER_START;
-
- laser.x1 = screen->w / 2;
- laser.y1 = screen->h;
-
- laser.x2 = laser.x1;
- laser.y2 = 0;
-
- playsound(SND_LASER);
- playsound(SND_BUZZ);
-
- if ((rand() % 10) < 5)
- tux_img = IMG_TUX_DRAT;
- else
- tux_img = IMG_TUX_YIPE;
- }
+ if ((rand() % 10) < 5)
+ tux_img = IMG_TUX_DRAT;
+ else
+ tux_img = IMG_TUX_YIPE;
+ }
-
- /* Clear digits: */
-
- digits[0] = 0;
- digits[1] = 0;
- digits[2] = 0;
- neg_answer_picked = 0;
- }
+ /* Clear digits: */
+ digits[0] = 0;
+ digits[1] = 0;
+ digits[2] = 0;
+ neg_answer_picked = 0;
+}
-
- /* Handle start-wait countdown: */
-
- if (level_start_wait > 0)
- {
- level_start_wait--;
+void game_countdown(void)
+{
+ level_start_wait--;
+ if (level_start_wait > LEVEL_START_WAIT_START / 4)
+ tux_img = IMG_TUX_RELAX1;
+ else if (level_start_wait > 0)
+ tux_img = IMG_TUX_RELAX2;
+ else
+ tux_img = IMG_TUX_SIT;
- if (level_start_wait > LEVEL_START_WAIT_START / 4)
- tux_img = IMG_TUX_RELAX1;
- else if (level_start_wait > 0)
- tux_img = IMG_TUX_RELAX2;
- else
- tux_img = IMG_TUX_SIT;
-
- if (level_start_wait == LEVEL_START_WAIT_START / 4)
- {
- playsound(SND_ALARM);
- }
- }
+ if (level_start_wait == LEVEL_START_WAIT_START / 4)
+ {
+ playsound(SND_ALARM);
+ }
+}
+void game_handle_tux(void)
+{
+ static int tux_same_counter;
+ /* If Tux pressed a button, pick a new (different!) stance: */
+ if (tux_pressing)
+ {
+
+ do
+ {
+ tux_img = IMG_TUX_CONSOLE1 + (rand() % 4);
+ }
+ while (tux_img == old_tux_img);
+
+ playsound(SND_CLICK);
+ }
+
+ /* If Tux is being animated, show the animation: */
+ if (tux_anim != -1)
+ {
+ tux_anim_frame--;
+ if (tux_anim_frame < 0)
+ tux_anim = -1;
+ else
+ tux_img = tux_anim + 1 - (tux_anim_frame / (ANIM_FRAME_START / 2));
+ }
+
+ /* Reset Tux to sitting if he's been doing nothing for a while: */
+ if (old_tux_img == tux_img)
+ {
+ tux_same_counter++;
+ if (tux_same_counter >= 20)
+ {
+ tux_img = IMG_TUX_SIT;
+ }
+ }
+ else
+ tux_same_counter = 0;
+}
+
+void game_handle_comets(void)
+{
+ int i;
+ /* Handle comets: */
+ num_comets_alive = 0;
- /* If Tux pressed a button, pick a new (different!) stance: */
-
- if (tux_pressing)
+ for (i = 0; i < MAX_COMETS; i++)
+ {
+ if (comets[i].alive)
+ {
+ num_comets_alive++;
+ /* update comet position */
+ comets[i].x = comets[i].x + 0; /* no lateral motion for now! */
+ comets[i].y = comets[i].y + (game_options->speed);
+
+ if (comets[i].y >= (screen->h - images[IMG_CITY_BLUE]->h) &&
+ comets[i].expl < COMET_EXPL_END)
{
- do
+ printf("\nAbout to disable shields or destroy city!\n");
+ /* Disable shields or destroy city: */
+ MC_AnsweredIncorrectly(&(comets[i].flashcard));
+ if (cities[comets[i].city].shields)
{
- tux_img = IMG_TUX_CONSOLE1 + (rand() % 4);
+ printf("\nDisabling shields!\n");
+ cities[comets[i].city].shields = 0;
+ playsound(SND_SHIELDSDOWN);
}
- while (tux_img == old_tux_img);
+ else
+ {
+ printf("\nDestroying city!\n");
+ cities[comets[i].city].expl = CITY_EXPL_START;
+ playsound(SND_EXPLOSION);
+ }
- playsound(SND_CLICK);
+ tux_anim = IMG_TUX_FIST1;
+ tux_anim_frame = ANIM_FRAME_START;
+
+ /* Destroy comet: */
+ comets[i].expl = COMET_EXPL_START;
}
-
-
- /* If Tux is being animated, show the animation: */
- if (tux_anim != -1)
+ /* Handle comet explosion animation: */
+ if (comets[i].expl >= COMET_EXPL_END)
{
- tux_anim_frame--;
-
- if (tux_anim_frame < 0)
- tux_anim = -1;
- else
- tux_img = tux_anim + 1 - (tux_anim_frame / (ANIM_FRAME_START / 2));
+ comets[i].expl--;
+ if (comets[i].expl < COMET_EXPL_END)
+ comets[i].alive = 0;
}
+ }
+ }
-
- /* Reset Tux to sitting if he's been doing nothing for a while: */
-
- if (old_tux_img == tux_img)
+ /* add more comets if needed: */
+ if (level_start_wait == 0 &&
+ (frame % 20) == 0 &&
+ gameover == 0)
+ {
+ /* num_attackers is how many comets are left in wave */
+ if (num_attackers > 0)
+ {
+ if ((rand() % 2) == 0 || num_comets_alive == 0)
{
- tux_same_counter++;
-
- if (tux_same_counter >= 20)
- {
- tux_img = IMG_TUX_SIT;
- }
+ if (add_comet())
+ {
+ num_attackers--;
+ }
}
- else
- tux_same_counter = 0;
+ }
+ else
+ {
+ if (num_comets_alive == 0)
+ {
+ /* Time for the next wave! */
-
- /* Handle comets: */
-
- num_comets_alive = 0;
-
- for (i = 0; i < MAX_COMETS; i++)
+ /* FIXME: End of level stuff goes here */
+ /* FIXME this belongs in game_handle_cities(), I think */
+ if (num_cities_alive > 0)
{
- if (comets[i].alive)
- {
- num_comets_alive++;
-
- comets[i].x = comets[i].x + 0;
- comets[i].y = comets[i].y + (game_options->speed * wave);
-
- if (comets[i].y >= (screen->h - images[IMG_CITY_BLUE]->h) &&
- comets[i].expl < COMET_EXPL_END)
- {
- /* Disable shields or destroy city: */
-
- if (cities[comets[i].city].shields)
- {
- cities[comets[i].city].shields = 0;
- playsound(SND_SHIELDSDOWN);
- }
- else
- {
- cities[comets[i].city].expl = CITY_EXPL_START;
- playsound(SND_EXPLOSION);
- }
-
- tux_anim = IMG_TUX_FIST1;
- tux_anim_frame = ANIM_FRAME_START;
-
-
- /* Destroy comet: */
-
- comets[i].expl = COMET_EXPL_START;
- }
-
-
- /* Handle comet explosion animation: */
-
- if (comets[i].expl >= COMET_EXPL_END)
- {
- comets[i].expl--;
-
- if (comets[i].expl < COMET_EXPL_END)
- comets[i].alive = 0;
- }
- }
+ /* Go on to the next wave: */
+ wave++;
+ reset_level();
}
-
-
- /* Handle laser: */
-
- if (laser.alive > 0)
- laser.alive--;
-
-
- /* Comet time! */
-
- if (level_start_wait == 0 && (frame % 20) == 0 &&
- gameover == 0)
- {
- if (num_attackers > 0)
- {
- /* More comets to add during this wave! */
-
- if ((rand() % 2) == 0 || num_comets_alive == 0)
- {
- add_comet();
- num_attackers--;
- }
- }
else
{
- if (num_comets_alive == 0)
- {
- /* Time for the next wave! */
-
- /* FIXME: End of level stuff goes here */
-
- if (num_cities_alive > 0)
- {
- /* Go on to the next wave: */
-
- wave++;
- reset_level();
- }
- else
- {
- /* No more cities! Game over! */
-
- gameover = GAMEOVER_COUNTER_START;
- }
- }
+ /* No more cities! Game over! */
+ gameover = GAMEOVER_COUNTER_START;
}
}
+ }
+ }
+}
+void game_handle_cities(void)
+{
+ int i;
+ /* FIXME does the following variable do anything? */
+ num_cities_alive = 0;
- /* Handle cities: */
-
- num_cities_alive = 0;
-
- for (i = 0; i < NUM_CITIES; i++)
- {
- if (cities[i].alive)
- {
- num_cities_alive++;
-
-
- /* Handle animated explosion: */
-
- if (cities[i].expl)
- {
- cities[i].expl--;
-
- if (cities[i].expl == 0)
- cities[i].alive = 0;
- }
- }
- }
-
-
- /* Handle game-over: */
-
- if (gameover > 0)
+ for (i = 0; i < NUM_CITIES; i++)
+ {
+ if (cities[i].alive)
+ {
+ num_cities_alive++;
+ /* Handle animated explosion: */
+ if (cities[i].expl)
{
- gameover--;
-
- if (gameover <= 0)
- done = 1;
+ cities[i].expl--;
+ if (cities[i].expl == 0)
+ cities[i].alive = 0;
}
-
-
- /* Clear screen: */
-
- if (bkgd == NULL)
- {
- dest.x = 0;
- dest.y = 0;
- dest.w = screen->w;
- dest.h = ((screen->h) / 4) * 3;
+ }
+ }
+}
- SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format,
- 64,
- 64 + ((wave * 32) % 192),
- 128 - ((wave * 16) % 128)));
+void game_draw(void)
+{
+ int i,j, img;
+ SDL_Rect src, dest;
+ char str[64];
+ char* comet_str;
+ /* Clear screen: */
+ if (bkgd == NULL)
+ {
+ dest.x = 0;
+ dest.y = 0;
+ dest.w = screen->w;
+ dest.h = ((screen->h) / 4) * 3;
- dest.x = 0;
- dest.y = ((screen->h) / 4) * 3;
- dest.w = screen->w;
- dest.h = (screen->h) / 4;
+ SDL_FillRect(screen, &dest,
+ SDL_MapRGB(screen->format,
+ 64,
+ 64 + ((wave * 32) % 192),
+ 128 - ((wave * 16) % 128)));
- SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 64, 96, 64));
- }
- else
- SDL_BlitSurface(bkgd, NULL, screen, NULL);
+ dest.x = 0;
+ dest.y = ((screen->h) / 4) * 3;
+ dest.w = screen->w;
+ dest.h = (screen->h) / 4;
+ SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 64, 96, 64));
+ }
+ else
+ {
+ SDL_BlitSurface(bkgd, NULL, screen, NULL);
+ }
- /* Draw "Demo" */
+ /* Draw "Demo" */
+ if (game_options->demo_mode)
+ {
+ dest.x = (screen->w - images[IMG_DEMO]->w) / 2;
+ dest.y = (screen->h - images[IMG_DEMO]->h) / 2;
+ dest.w = images[IMG_DEMO]->w;
+ dest.h = images[IMG_DEMO]->h;
- if (game_options->demo_mode)
- {
- dest.x = (screen->w - images[IMG_DEMO]->w) / 2;
- dest.y = (screen->h - images[IMG_DEMO]->h) / 2;
- dest.w = images[IMG_DEMO]->w;
- dest.h = images[IMG_DEMO]->h;
+ SDL_BlitSurface(images[IMG_DEMO], NULL, screen, &dest);
+ }
- SDL_BlitSurface(images[IMG_DEMO], NULL, screen, &dest);
- }
+ /* Draw wave: */
+ dest.x = 0;
+ dest.y = 0;
+ dest.w = images[IMG_WAVE]->w;
+ dest.h = images[IMG_WAVE]->h;
+ SDL_BlitSurface(images[IMG_WAVE], NULL, screen, &dest);
- /* Draw wave: */
+ sprintf(str, "%d", wave);
+ draw_numbers(str, images[IMG_WAVE]->w + (images[IMG_NUMBERS]->w / 10));
- dest.x = 0;
- dest.y = 0;
- dest.w = images[IMG_WAVE]->w;
- dest.h = images[IMG_WAVE]->h;
-
- SDL_BlitSurface(images[IMG_WAVE], NULL, screen, &dest);
-
- sprintf(str, "%d", wave);
- draw_numbers(str, images[IMG_WAVE]->w + (images[IMG_NUMBERS]->w / 10));
-
-
- /* Draw score: */
-
- dest.x = (screen->w - ((images[IMG_NUMBERS]->w / 10) * 7) -
+ /* Draw score: */
+ dest.x = (screen->w - ((images[IMG_NUMBERS]->w / 10) * 7) -
images[IMG_SCORE]->w);
- dest.y = 0;
- dest.w = images[IMG_SCORE]->w;
- dest.h = images[IMG_SCORE]->h;
+ dest.y = 0;
+ dest.w = images[IMG_SCORE]->w;
+ dest.h = images[IMG_SCORE]->h;
- SDL_BlitSurface(images[IMG_SCORE], NULL, screen, &dest);
+ SDL_BlitSurface(images[IMG_SCORE], NULL, screen, &dest);
- sprintf(str, "%.6d", score);
- draw_numbers(str, screen->w - ((images[IMG_NUMBERS]->w / 10) * 6));
+ sprintf(str, "%.6d", score);
+ draw_numbers(str, screen->w - ((images[IMG_NUMBERS]->w / 10) * 6));
-
- /* Draw cities: */
-
- for (i = 0; i < NUM_CITIES; i++)
- {
- /* Decide which image to display: */
-
- if (cities[i].alive)
- {
- if (cities[i].expl == 0)
- img = IMG_CITY_BLUE;
- else
- img = (IMG_CITY_BLUE_EXPL5 -
- (cities[i].expl / (CITY_EXPL_START / 5)));
- }
- else
- img = IMG_CITY_BLUE_DEAD;
+ /* Draw cities: */
+ for (i = 0; i < NUM_CITIES; i++)
+ {
+ /* Decide which image to display: */
+ if (cities[i].alive)
+ {
+ if (cities[i].expl == 0)
+ img = IMG_CITY_BLUE;
+ else
+ img = (IMG_CITY_BLUE_EXPL5 - (cities[i].expl / (CITY_EXPL_START / 5)));
+ }
+ else
+ {
+ img = IMG_CITY_BLUE_DEAD;
+ }
+ /* Change image to appropriate color: */
+ img = img + ((wave % MAX_CITY_COLORS) *
+ (IMG_CITY_GREEN - IMG_CITY_BLUE));
-
- /* Change image to appropriate color: */
-
- img = img + ((wave % MAX_CITY_COLORS) *
- (IMG_CITY_GREEN - IMG_CITY_BLUE));
- /* img = img + ((i % MAX_CITY_COLORS) *
- (IMG_CITY_GREEN - IMG_CITY_BLUE)); */
-
-
- /* Draw it! */
-
- dest.x = cities[i].x - (images[img]->w / 2);
- dest.y = (screen->h) - (images[img]->h);
- dest.w = (images[img]->w);
- dest.h = (images[img]->h);
-
- SDL_BlitSurface(images[img], NULL,
- screen, &dest);
+ /* Draw it! */
+ dest.x = cities[i].x - (images[img]->w / 2);
+ dest.y = (screen->h) - (images[img]->h);
+ dest.w = (images[img]->w);
+ dest.h = (images[img]->h);
+
+ SDL_BlitSurface(images[img], NULL, screen, &dest);
-
- /* Draw sheilds: */
-
- if (cities[i].shields)
- {
- for (j = (frame % 3); j < images[IMG_SHIELDS]->h; j = j + 3)
- {
+ /* Draw sheilds: */
+ if (cities[i].shields)
+ {
+ for (j = (frame % 3); j < images[IMG_SHIELDS]->h; j = j + 3)
+ {
src.x = 0;
src.y = j;
src.w = images[IMG_SHIELDS]->w;
@@ -726,170 +871,233 @@
dest.h = src.h;
SDL_BlitSurface(images[IMG_SHIELDS], &src, screen, &dest);
- }
- }
- }
+ }
+ }
+ }
- /* Draw comets: */
-
- for (i = 0; i < MAX_COMETS; i++)
- {
- if (comets[i].alive)
- {
- if (comets[i].expl < COMET_EXPL_END)
- {
- /* Decide which image to display: */
- img = IMG_COMET1 + ((frame + i) % 3);
-
- /* Display the formula (flashing, in the bottom half
+ /* Draw comets: */
+ for (i = 0; i < MAX_COMETS; i++)
+ {
+ if (comets[i].alive)
+ {
+ if (comets[i].expl < COMET_EXPL_END)
+ {
+ /* Decide which image to display: */
+ img = IMG_COMET1 + ((frame + i) % 3);
+ /* Display the formula (flashing, in the bottom half
of the screen) */
- if (comets[i].y < screen->h / 2 || frame % 8 < 6)
- comet_str = comets[i].formula;
- else comet_str = NULL;
- }
- else
- {
- img = comets[i].expl;
- comet_str = comets[i].answer_str;
- }
+ if (comets[i].y < screen->h / 2 || frame % 8 < 6)
+ {
+ comet_str = comets[i].formula;
+ }
+ else
+ {
+ comet_str = NULL;
+ }
+ }
+ else
+ {
+ img = comets[i].expl;
+ comet_str = comets[i].answer_str;
+ }
- /* Draw it! */
- dest.x = comets[i].x - (images[img]->w / 2);
- dest.y = comets[i].y - images[img]->h;
- dest.w = images[img]->w;
- dest.h = images[img]->h;
+ /* Draw it! */
+ dest.x = comets[i].x - (images[img]->w / 2);
+ dest.y = comets[i].y - images[img]->h;
+ dest.w = images[img]->w;
+ dest.h = images[img]->h;
- SDL_BlitSurface(images[img], NULL, screen, &dest);
- if (comet_str != NULL)
- draw_nums(comet_str, comets[i].x, comets[i].y);
- }
- }
+ SDL_BlitSurface(images[img], NULL, screen, &dest);
+ if (comet_str != NULL)
+ {
+ draw_nums(comet_str, comets[i].x, comets[i].y);
+ }
+ }
+ }
-
- /* Draw laser: */
-
- if (laser.alive)
- {
- draw_line(laser.x1, laser.y1, laser.x2, laser.y2,
+ /* Draw laser: */
+ if (laser.alive)
+ {
+ draw_line(laser.x1, laser.y1, laser.x2, laser.y2,
255 / (LASER_START - laser.alive),
192 / (LASER_START - laser.alive),
64);
- }
+ }
+ /* Draw numeric keypad: */
+ if (game_options->use_keypad)
+ {
+ /* pick image to draw: */
+ int keypad_image;
+ if (MC_AllowNegAnswer())
+ {
+ /* draw regular keypad */
+ keypad_image = IMG_KEYPAD;
+ }
+ else
+ {
+ /* draw keypad with with grayed-out '+' and '-' */
+ keypad_image = IMG_KEYPAD_NO_NEG;
+ }
- /* Draw numeric keypad: */
- if (game_options->use_keypad)
- {
- if (math_options->allow_neg_answer)
- /* draw regular keypad */
- {
- dest.x = (screen->w - images[IMG_KEYPAD]->w) / 2;
- dest.y = (screen->h - images[IMG_KEYPAD]->h) / 2;
- dest.w = images[IMG_KEYPAD]->w;
- dest.h = images[IMG_KEYPAD]->h;
- SDL_BlitSurface(images[IMG_KEYPAD], NULL, screen, &dest);
- }
- else
- /* draw keypad with with grayed-out '+' and '-' */
- {
- dest.x = (screen->w - images[IMG_KEYPAD_NO_NEG]->w) / 2;
- dest.y = (screen->h - images[IMG_KEYPAD_NO_NEG]->h) / 2;
- dest.w = images[IMG_KEYPAD_NO_NEG]->w;
- dest.h = images[IMG_KEYPAD_NO_NEG]->h;
- SDL_BlitSurface(images[IMG_KEYPAD_NO_NEG], NULL, screen, &dest);
- }
- }
+ /* now draw it: */
+ dest.x = (screen->w - images[keypad_image]->w) / 2;
+ dest.y = (screen->h - images[keypad_image]->h) / 2;
+ dest.w = images[keypad_image]->w;
+ dest.h = images[keypad_image]->h;
+ SDL_BlitSurface(images[keypad_image], NULL, screen, &dest);
+ }
+ /* Draw console & tux: */
+ /* FIXME add counter for remaining questions */
+ draw_console_image(IMG_CONSOLE);
- /* Draw console & tux: */
+ if (gameover > 0)
+ {
+ tux_img = IMG_TUX_FIST1 + ((frame / 2) % 2);
+ }
+ draw_console_image(tux_img);
- draw_console_image(IMG_CONSOLE);
+ /* LED code moved into separate function by DSB */
+ draw_led_nums();
- if (gameover > 0)
- {
- tux_img = IMG_TUX_FIST1 + ((frame / 2) % 2);
- }
+ /* Draw "Game Over" */
+ if (gameover > 0)
+ {
+ dest.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
+ dest.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
+ dest.w = images[IMG_GAMEOVER]->w;
+ dest.h = images[IMG_GAMEOVER]->h;
+
+ SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest);
+ }
+
+ /* Swap buffers: */
+ SDL_Flip(screen);
+}
- draw_console_image(tux_img);
+int check_exit_conditions(void)
+{
+ if (SDL_quit_received)
+ {
+ return GAME_OVER_WINDOW_CLOSE;
+ }
- /* LED code moved into separate function by DSB */
- draw_led_nums();
+ if (escape_received)
+ {
+ return GAME_OVER_ESCAPE;
+ }
- /* Draw "Game Over" */
+ /* determine if game lost (i.e. all cities blown up): */
+ {
+ int i;
+ int surviving_cities = 0;
- if (gameover > 0)
+ for (i = 0; i < NUM_CITIES; i++)
+ {
+ if (cities[i].alive)
{
- dest.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
- dest.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
- dest.w = images[IMG_GAMEOVER]->w;
- dest.h = images[IMG_GAMEOVER]->h;
-
- SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest);
+ surviving_cities++;
}
-
-
- /* Swap buffers: */
-
- SDL_Flip(screen);
+ }
+ if (!surviving_cities)
+ {
+ return GAME_OVER_LOST;
+ }
+ }
- /* If we're in "PAUSE" mode, pause! */
+ /* determine if game won (i.e. all questions in mission answered correctly): */
+ if (MC_MissionAccomplished())
+ {
+ return GAME_OVER_WON;
+ }
- if (paused)
- {
- quit = pause_game();
- paused = 0;
- }
+ /* Could have situation where mathcards doesn't have more questions */
+ /* even though not all questions answered correctly: */
+ if (MC_GameOver())
+ {
+ return GAME_OVER_OTHER;
+ }
-
- /* Keep playing music: */
-
-#ifndef NOSOUND
- if (game_options->use_sound)
- {
- if (!Mix_PlayingMusic())
- Mix_PlayMusic(musics[MUS_GAME + (rand() % 3)], 0);
- }
-#endif
-
-
- /* Pause (keep frame-rate event) */
-
- now_time = SDL_GetTicks();
- if (now_time < last_time + FPS)
- SDL_Delay(last_time + FPS - now_time);
- }
- while (!done && !quit);
+ /* Need to get out if no comets alive and MathCards out of unanswered questions, */
+ /* even though MathCards thinks there are still questions "in play". */
+ /* This SHOULD NOT HAPPEN and means we have a bug somewhere. */
-
- /* Free background: */
+ {
+// int i;
+// int living_comets = 0;
+//
+// for (i = 0; i < MAX_COMETS; i++)
+// {
+// living_comets += comets[i].alive;
+// }
- if (bkgd != NULL)
- SDL_FreeSurface(bkgd);
+ if (MC_NoQuestionsLeft() && !num_comets_alive)
+ {
+ #ifdef TUXMATH_DEBUG
+ printf("\nNoQuestionsLeft() = %d", MC_NoQuestionsLeft());
+ printf("\nnum_comets_alive = %d", num_comets_alive);
+ #endif
+ return GAME_OVER_ERROR;
+ }
+ }
+ /* if we made it to here, the game goes on! */
+ return GAME_IN_PROGRESS;
+}
-
- /* Stop music: */
-#ifndef NOSOUND
- if (game_options->use_sound)
+void print_exit_conditions(void)
+{
+ printf("\ngame_status:\t");
+ switch (game_status)
{
- if (Mix_PlayingMusic())
+ case GAME_IN_PROGRESS:
{
- Mix_HaltMusic();
+ printf("GAME_IN_PROGRESS\n");
+ break;
}
+
+ case GAME_OVER_WON:
+ {
+ printf("GAME_OVER_WON\n");
+ break;
+ }
+ case GAME_OVER_LOST:
+ {
+ printf("GAME_OVER_LOST\n");
+ break;
+ }
+ case GAME_OVER_OTHER:
+ {
+ printf("GAME_OVER_OTHER\n");
+ break;
+ }
+ case GAME_OVER_ESCAPE:
+ {
+ printf("GAME_OVER_ESCAPE\n");
+ break;
+ }
+ case GAME_OVER_WINDOW_CLOSE:
+ {
+ printf("GAME_OVER_WINDOW_CLOSE\n");
+ break;
+ }
+ case GAME_OVER_ERROR:
+ {
+ printf("GAME_OVER_ERROR\n");
+ break;
+ }
+ default:
+ {
+ printf("Unrecognized value\n");
+ break;
+ }
}
-#endif
-
-
- /* Return the chosen command: */
-
- return quit;
}
-
/* Reset stuff for the next level! */
-
void reset_level(void)
{
char fname[1024];
@@ -899,9 +1107,11 @@
/* Clear all comets: */
for (i = 0; i < MAX_COMETS; i++)
+ {
comets[i].alive = 0;
-
-
+ }
+ num_comets_alive = 0;
+
/* Clear LED digits: */
digits[0] = 0;
@@ -948,14 +1158,30 @@
/* Set number of attackers for this wave: */
+ /* num_attackers = 2 * wave; */
- num_attackers = 2 * wave; /* FIXME: Is this good? */
+ /* increase number of comets and speed if desired */
+ if (game_options->allow_speedup)
+ {
+ num_attackers = wave * game_options->extra_comets;
+ game_options->speed = (game_options->speed) * game_options->speedup_factor;
+
+ if (game_options->speed > game_options->max_speed)
+ {
+ game_options->speed = game_options->max_speed;
+ }
+ }
+ else
+ {
+ num_attackers = 2;
+ }
}
-/* Add an comet to the game (if there's room): */
-void add_comet(void)
+/* Add a comet to the game (if there's room): */
+
+int add_comet(void)
{
static int prev_city = -1;
int i, found;
@@ -966,129 +1192,66 @@
found = -1;
for (i = 0; i < MAX_COMETS && found == -1; i++)
+ {
+ if (comets[i].alive == 0)
{
- if (comets[i].alive == 0)
- {
- found = i;
- }
+ found = i;
}
+ }
if (found != -1)
+ {
+ /* Get math question for new comet: */
+ if (!MC_NextQuestion(&(comets[found].flashcard)))
+ /* no more questions - cannot create comet. */
{
- comets[found].alive = 1;
+ return 0;
+ }
+ /* FIXME streamline comet struct - for now assign to old variables */
+ /* FIXME also need to handle question formats */
+ comets[found].eq1 = comets[found].flashcard.num1;
+ comets[found].oper = comets[found].flashcard.operation;
+ comets[found].eq2 = comets[found].flashcard.num2;
+ comets[found].answer = comets[found].flashcard.num3;
- /* Pick a city to attack that was not attacked last time */
- /* (so formulas are less likely to overlap). */
- do
- i = rand() % NUM_CITIES;
- while (i == prev_city);
- prev_city = i;
-
+ comets[found].alive = 1;
+ num_comets_alive++;
- /* Set in to attack that city: */
-
- comets[found].city = i;
+ /* Pick a city to attack that was not attacked last time */
+ /* (so formulas are less likely to overlap). */
+ do
+ {
+ i = rand() % NUM_CITIES;
+ }
+ while (i == prev_city);
-
- /* Start at the top, above the city in question: */
-
- comets[found].x = cities[i].x;
- comets[found].y = 0;
-
-
- /* Pick an operation (+, -, *, /): */
- /* FIXME now that negative answers a possibility, */
- /* should test absolute value against max_answer */
- do
- {
- comets[found].oper = (rand() % NUM_OPERS);
- }
- while (opers[comets[found].oper] == 0);
+ prev_city = i;
-
- if (comets[found].oper == OPER_ADD)
- {
- /* Simple addition: */
+ /* Set in to attack that city: */
+ comets[found].city = i;
+ /* Start at the top, above the city in question: */
+ comets[found].x = cities[i].x;
+ comets[found].y = 0;
- do
- {
- comets[found].eq1 = pick_operand(0);
- comets[found].eq2 = pick_operand(0);
- comets[found].answer = comets[found].eq1 + comets[found].eq2;
- }
- while (comets[found].answer > math_options->max_answer);
- }
+ snprintf(comets[found].formula, FORMULA_LEN,"%d%c%d",
+ comets[found].eq1,
+ operchars[comets[found].oper],
+ comets[found].eq2);
+ snprintf(comets[found].answer_str, ANSWER_LEN, "%d",
+ comets[found].answer);
- else if (comets[found].oper == OPER_SUB)
- {
- /* Subtraction: */
-
- do
- {
- comets[found].eq1 = pick_operand(0);
- comets[found].eq2 = pick_operand(0);
- /* try again until answer not negative */
- /* unless neg_answer_allowed DSB */
- if (!math_options->allow_neg_answer)
- {
- do
- {
- comets[found].eq2 = pick_operand(0);
- }
- while (comets[found].eq2 > comets[found].eq1);
- }
- comets[found].answer = comets[found].eq1 - comets[found].eq2;
- }
- while (comets[found].answer > math_options->max_answer);
- }
-
- else if (comets[found].oper == OPER_MULT)
- {
- /* Multiplication: */
- do
- {
- comets[found].eq1 = pick_operand(1);
- comets[found].eq2 = pick_operand(0);
- comets[found].answer = comets[found].eq1 * comets[found].eq2;
- }
- while (comets[found].answer > math_options->max_answer);
- }
-
- else if (comets[found].oper == OPER_DIV)
- {
- /* Division: */
-
- do
- {
- /* (Don't divide by zero) */
-
- comets[found].eq2 = pick_operand(1);
-
-
- /* (Make sure answer will be a whole (int) number) */
-
- comets[found].eq1 = pick_operand(0) * comets[found].eq2;
-
-
- comets[found].answer = comets[found].eq1 / comets[found].eq2;
- }
- while (comets[found].answer > math_options->max_answer ||
- !in_range(comets[found].eq1 / 2));
- }
- snprintf(comets[found].formula, FORMULA_LEN,"%d%c%d",
- comets[found].eq1,
- operchars[comets[found].oper],
- comets[found].eq2);
- snprintf(comets[found].answer_str, ANSWER_LEN, "%d",
- comets[found].answer);
- }
+ /* comet slot found and question found so return successfully: */
+ return 1;
+ }
+ /* free comet slot not found - no comet added: */
+ return 0;
}
/* Draw numbers/symbols over the attacker: */
-
+/* This draw the numbers related to the comets */
void draw_nums(char * str, int x, int y)
{
int i, j, cur_x, c;
@@ -1213,12 +1376,14 @@
int pause_game(void)
{
- int done, quit;
+ /* NOTE - done and quit changed to pause_done and pause_quit */
+ /* due to potentially confusing name collision */
+ int pause_done, pause_quit;
SDL_Event event;
SDL_Rect dest;
- done = 0;
- quit = 0;
+ pause_done = 0;
+ pause_quit = 0;
dest.x = (screen->w - images[IMG_PAUSED]->w) / 2;
dest.y = (screen->h - images[IMG_PAUSED]->h) / 2;
@@ -1240,14 +1405,18 @@
while (SDL_PollEvent(&event))
{
if (event.type == SDL_KEYDOWN)
- done = 1;
+ pause_done = 1;
else if (event.type == SDL_QUIT)
- quit = 1;
+ {
+ SDL_quit_received = 1;
+ /* FIXME quit is being superseded */
+ pause_quit = 1;
+ }
}
SDL_Delay(100);
}
- while (!done && !quit);
+ while (!pause_done && !pause_quit);
#ifndef NOSOUND
@@ -1255,7 +1424,7 @@
Mix_ResumeMusic();
#endif
- return (quit);
+ return (pause_quit);
}
@@ -1401,7 +1570,7 @@
/* begin drawing so as to center display depending on whether minus */
/* sign needed (4 digit slots) or not (3 digit slots) DSB */
- if (math_options->allow_neg_answer)
+ if (MC_AllowNegAnswer())
dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 4) / 2);
else
dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 3) / 2);
@@ -1411,7 +1580,7 @@
{
if (-1 == i)
{
- if (math_options->allow_neg_answer)
+ if (MC_AllowNegAnswer())
{
if (neg_answer_picked)
src.x = (images[IMG_LED_NEG_SIGN]->w) / 2;
@@ -1451,10 +1620,10 @@
}
/* Translates mouse events into keyboard events when on-screen keypad used */
-void game_mouse_event(SDL_Event the_event)
+void game_mouse_event(SDL_Event event)
{
int keypad_w, keypad_h, x, y, row, column;
- SDLKey keypad_key = SDLK_UNKNOWN;
+ SDLKey key = SDLK_UNKNOWN;
keypad_w = 0;
keypad_h = 0;
@@ -1469,7 +1638,7 @@
/* make sure keypad image is valid and has non-zero dimensions: */
/* FIXME maybe this checking should be done once at the start */
/* of game() rather than with every mouse click */
- if (math_options->allow_neg_answer)
+ if (MC_AllowNegAnswer())
{
if (!images[IMG_KEYPAD])
return;
@@ -1497,13 +1666,13 @@
}
/* only proceed if click falls within keypad: */
- if (!((the_event.button.x >=
+ if (!((event.button.x >=
(screen->w / 2) - (keypad_w / 2) &&
- the_event.button.x <=
+ event.button.x <=
(screen->w / 2) + (keypad_w / 2) &&
- the_event.button.y >=
+ event.button.y >=
(screen->h / 2) - (keypad_h / 2) &&
- the_event.button.y <=
+ event.button.y <=
(screen->h / 2) + (keypad_h / 2))))
/* click outside of keypad - do nothing */
{
@@ -1512,8 +1681,8 @@
else /* click was within keypad */
{
- x = (the_event.button.x - ((screen->w / 2) - (keypad_w / 2)));
- y = (the_event.button.y - ((screen->h / 2) - (keypad_h / 2)));
+ x = (event.button.x - ((screen->w / 2) - (keypad_w / 2)));
+ y = (event.button.y - ((screen->h / 2) - (keypad_h / 2)));
/* Now determine what onscreen key was pressed */
/* */
@@ -1559,69 +1728,71 @@
if (0 == row)
{
if (0 == column)
- keypad_key = SDLK_7;
+ key = SDLK_7;
if (1 == column)
- keypad_key = SDLK_8;
+ key = SDLK_8;
if (2 == column)
- keypad_key = SDLK_9;
+ key = SDLK_9;
if (3 == column)
- keypad_key = SDLK_MINUS;
+ key = SDLK_MINUS;
}
if (1 == row)
{
if (0 == column)
- keypad_key = SDLK_4;
+ key = SDLK_4;
if (1 == column)
- keypad_key = SDLK_5;
+ key = SDLK_5;
if (2 == column)
- keypad_key = SDLK_6;
+ key = SDLK_6;
if (3 == column)
- keypad_key = SDLK_PLUS;
+ key = SDLK_PLUS;
}
if (2 == row)
{
if (0 == column)
- keypad_key = SDLK_1;
+ key = SDLK_1;
if (1 == column)
- keypad_key = SDLK_2;
+ key = SDLK_2;
if (2 == column)
- keypad_key = SDLK_3;
+ key = SDLK_3;
if (3 == column)
- keypad_key = SDLK_PLUS;
+ key = SDLK_PLUS;
}
if (3 == row)
{
if (0 == column)
- keypad_key = SDLK_0;
+ key = SDLK_0;
if (1 == column)
- keypad_key = SDLK_RETURN;
+ key = SDLK_RETURN;
if (2 == column)
- keypad_key = SDLK_RETURN;
+ key = SDLK_RETURN;
if (3 == column)
- keypad_key = SDLK_RETURN;
+ key = SDLK_RETURN;
}
- if (keypad_key == SDLK_UNKNOWN)
+ if (key == SDLK_UNKNOWN)
{
return;
}
/* now can proceed as if keyboard was used */
- game_key_event(keypad_key);
+ game_key_event(key);
}
}
/* called by either key presses or mouse clicks on */
/* on-screen keypad */
-void game_key_event(SDLKey pressed_key)
+void game_key_event(SDLKey key)
{
- if (pressed_key == SDLK_ESCAPE)
+ if (key == SDLK_ESCAPE)
{
/* Escape key - quit! */
- done = 1;
+ escape_received = 1;
+ /* FIXME done will be superseded */
+ done = 1;
}
- else if (pressed_key == SDLK_TAB
- || pressed_key == SDLK_p)
+ else if (key == SDLK_TAB
+ || key == SDLK_p)
{
/* [TAB] or [P]: Pause! */
paused = 1;
@@ -1631,43 +1802,43 @@
{
/* Eat other keys until level start wait has passed,
or if game is in demo mode: */
- pressed_key = SDLK_UNKNOWN;
+ key = SDLK_UNKNOWN;
}
- if (pressed_key >= SDLK_0 && pressed_key <= SDLK_9)
+ if (key >= SDLK_0 && key <= SDLK_9)
{
/* [0]-[9]: Add a new digit: */
digits[0] = digits[1];
digits[1] = digits[2];
- digits[2] = pressed_key - SDLK_0;
+ digits[2] = key - SDLK_0;
tux_pressing = 1;
}
- else if (pressed_key >= SDLK_KP0 && pressed_key <= SDLK_KP9)
+ else if (key >= SDLK_KP0 && key <= SDLK_KP9)
{
/* Keypad [0]-[9]: Add a new digit: */
digits[0] = digits[1];
digits[1] = digits[2];
- digits[2] = pressed_key - SDLK_KP0;
+ digits[2] = key - SDLK_KP0;
tux_pressing = 1;
}
/* support for negative answer input DSB */
- else if ((pressed_key == SDLK_MINUS || pressed_key == SDLK_KP_MINUS)
- && math_options->allow_neg_answer) /* do nothing unless neg answers allowed */
+ else if ((key == SDLK_MINUS || key == SDLK_KP_MINUS)
+ && MC_AllowNegAnswer()) /* do nothing unless neg answers allowed */
{
/* allow player to make answer negative: */
neg_answer_picked = 1;
tux_pressing = 1;
}
- else if ((pressed_key == SDLK_PLUS || pressed_key == SDLK_KP_PLUS)
- && math_options->allow_neg_answer) /* do nothing unless neg answers allowed */
+ else if ((key == SDLK_PLUS || key == SDLK_KP_PLUS)
+ && MC_AllowNegAnswer()) /* do nothing unless neg answers allowed */
{
/* allow player to make answer positive: */
neg_answer_picked = 0;
tux_pressing = 1;
}
- else if (pressed_key == SDLK_BACKSPACE ||
- pressed_key == SDLK_CLEAR ||
- pressed_key == SDLK_DELETE)
+ else if (key == SDLK_BACKSPACE ||
+ key == SDLK_CLEAR ||
+ key == SDLK_DELETE)
{
/* [BKSP]: Clear digits! */
digits[0] = 0;
@@ -1675,9 +1846,9 @@
digits[2] = 0;
tux_pressing = 1;
}
- else if (pressed_key == SDLK_RETURN ||
- pressed_key == SDLK_KP_ENTER ||
- pressed_key == SDLK_SPACE)
+ else if (key == SDLK_RETURN ||
+ key == SDLK_KP_ENTER ||
+ key == SDLK_SPACE)
{
/* [ENTER]: Accept digits! */
doing_answer = 1;
@@ -1728,3 +1899,21 @@
return ok;
}
+void reset_comets(void)
+{
+ int i =0;
+ for (i = 0; i < MAX_COMETS; i++)
+ {
+ comets[i].alive = 0;
+ comets[i].expl = 0;
+ comets[i].city = 0;
+ comets[i].x = 0;
+ comets[i].y = 0;
+ comets[i].eq1 = 0;
+ comets[i].oper = 0;
+ comets[i].eq2 = 0;
+ comets[i].formula[FORMULA_LEN] = "";
+ comets[i].answer = 0;
+ comets[i].answer_str[ANSWER_LEN] = "";
+ }
+}
Modified: tuxmath/trunk/src/game.h
===================================================================
--- tuxmath/trunk/src/game.h 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/game.h 2006-05-16 17:05:20 UTC (rev 10)
@@ -58,8 +58,25 @@
NUM_Q_RANGES
};
+typedef struct range_type {
+ int min;
+ int max;
+} range_type;
+
+extern range_type ranges[NUM_Q_RANGES];
+
+enum {
+ GAME_IN_PROGRESS,
+ GAME_OVER_WON,
+ GAME_OVER_LOST,
+ GAME_OVER_OTHER,
+ GAME_OVER_ESCAPE,
+ GAME_OVER_WINDOW_CLOSE,
+ GAME_OVER_ERROR
+};
+
int game(void);
-
+/* draw_nums() is used in options.c so need extern linkage */
void draw_nums(char* str, int x, int y);
#endif
Added: tuxmath/trunk/src/mathcards.c
===================================================================
--- tuxmath/trunk/src/mathcards.c 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/mathcards.c 2006-05-16 17:05:20 UTC (rev 10)
@@ -0,0 +1,1952 @@
+/*
+* 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).
+*
+*/
+
+#include "mathcards.h"
+
+/* "Globals" for mathcards.c: */
+MC_Options* math_opts = 0;
+MC_MathQuestion* question_list = 0;
+MC_MathQuestion* wrong_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;
+
+/* "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 MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f);
+static MC_MathQuestion* create_node_from_card(MC_FlashCard* card);
+static MC_FlashCard* create_card_from_node(MC_MathQuestion* node);
+static MC_MathQuestion* create_node_copy(MC_MathQuestion* other);
+static int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy);
+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 list_length(MC_MathQuestion* list);
+
+static MC_MathQuestion* randomize_list(MC_MathQuestion* list);
+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 void print_math_options(void);
+static void print_list(MC_MathQuestion* list);
+static void print_node(MC_MathQuestion* ptr);
+static void print_card(MC_FlashCard card);
+static void print_counters(void);
+
+/* FIXME: Program should load options from disk */
+/* 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)
+{
+ #ifdef MC_DEBUG
+ printf("\nEntering MC_Initialize()");
+ #endif
+
+ /* check flag to see if we did this already */
+ if (initialized)
+ {
+
+ #ifdef MC_DEBUG
+ printf("\nAlready initialized");
+ print_math_options();
+ printf("\nLeaving MC_Initialize()\n");
+ #endif
+
+ return 1;
+ }
+ math_opts = malloc(sizeof(MC_Options));
+ /* bail out if no struct */
+ if (!math_opts)
+ {
+
+ #ifdef MC_DEBUG
+ printf("\nError: math_opts null or invalid");
+ printf("\nLeaving MC_Initialize()\n");
+ #endif
+
+ fprintf(stderr, "\nUnable to initialize math_options");
+ return 0;
+ }
+
+ /* set general math options */
+ math_opts->allow_neg_answer = DEFAULT_ALLOW_NEG_ANSWER;
+ math_opts->max_answer = DEFAULT_MAX_ANSWER;
+ math_opts->max_questions = DEFAULT_MAX_QUESTIONS;
+ math_opts->recycle_corrects = DEFAULT_RECYCLE_CORRECTS;
+ math_opts->recycle_wrongs = DEFAULT_RECYCLE_WRONGS;
+ math_opts->copies_recycled_wrongs = DEFAULT_COPIES_RECYCLED_WRONGS;
+ math_opts->format_answer_last = DEFAULT_FORMAT_ANSWER_LAST;
+ math_opts->format_answer_first = DEFAULT_FORMAT_ANSWER_FIRST;
+ math_opts->format_answer_middle = DEFAULT_FORMAT_ANSWER_MIDDLE;
+ math_opts->question_copies = DEFAULT_QUESTION_COPIES;
+ math_opts->randomize = DEFAULT_RANDOMIZE;
+ /* set addition options */
+ math_opts->addition_allowed = DEFAULT_ADDITION_ALLOWED;
+ math_opts->min_augend = DEFAULT_MIN_AUGEND;
+ math_opts->max_augend = DEFAULT_MAX_AUGEND;
+ math_opts->min_addend = DEFAULT_MIN_ADDEND;
+ math_opts->max_addend = DEFAULT_MAX_ADDEND;
+ /* set subtraction options */
+ math_opts->subtraction_allowed = DEFAULT_SUBTRACTION_ALLOWED;
+ math_opts->min_minuend = DEFAULT_MIN_MINUEND;
+ math_opts->max_minuend = DEFAULT_MAX_MINUEND;
+ math_opts->min_subtrahend = DEFAULT_MIN_SUBTRAHEND;
+ math_opts->max_subtrahend = DEFAULT_MAX_SUBTRAHEND;
+ /* set multiplication options */
+ math_opts->multiplication_allowed = DEFAULT_MULTIPLICATION_ALLOWED;
+ math_opts->min_multiplier = DEFAULT_MIN_MULTIPLIER;
+ math_opts->max_multiplier = DEFAULT_MAX_MULTIPLIER;
+ math_opts->min_multiplicand = DEFAULT_MIN_MULTIPLICAND;
+ math_opts->max_multiplicand = DEFAULT_MAX_MULTIPLICAND;
+ /* set division options */
+ math_opts->division_allowed = DEFAULT_DIVISION_ALLOWED;
+ math_opts->min_divisor = DEFAULT_MIN_DIVISOR;
+ math_opts->max_divisor = DEFAULT_MAX_DIVISOR;
+ math_opts->min_quotient = DEFAULT_MIN_QUOTIENT;
+ math_opts->max_quotient = DEFAULT_MAX_QUOTIENT;
+
+ /* if no negatives to be used, reset any negatives to 0 */
+ if (!math_opts->allow_neg_answer)
+ {
+ if (math_opts->min_augend < 0)
+ math_opts->min_augend = 0;
+ if (math_opts->max_augend < 0)
+ math_opts->max_augend = 0;
+ if (math_opts->min_addend < 0)
+ math_opts->min_addend = 0;
+ if (math_opts->max_addend < 0)
+ math_opts->max_addend = 0;
+
+ if (math_opts->min_minuend < 0)
+ math_opts->min_minuend = 0;
+ if (math_opts->max_minuend < 0)
+ math_opts->max_minuend = 0;
+ if (math_opts->min_subtrahend < 0)
+ math_opts->min_subtrahend = 0;
+ if (math_opts->max_subtrahend < 0)
+ math_opts->max_subtrahend = 0;
+
+ if (math_opts->min_multiplier < 0)
+ math_opts->min_multiplier = 0;
+ if (math_opts->max_multiplier < 0)
+ math_opts->max_multiplier = 0;
+ if (math_opts->min_multiplicand < 0)
+ math_opts->min_multiplicand = 0;
+ if (math_opts->max_multiplicand < 0)
+ math_opts->max_multiplicand = 0;
+
+ if (math_opts->min_divisor < 0)
+ math_opts->min_divisor = 0;
+ if (math_opts->max_divisor < 0)
+ math_opts->max_divisor = 0;
+ if (math_opts->min_quotient < 0)
+ math_opts->min_quotient = 0;
+ if (math_opts->max_quotient < 0)
+ math_opts->max_quotient = 0;
+ }
+ initialized = 1;
+
+ #ifdef MC_DEBUG
+ print_math_options();
+ 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)
+{
+ #ifdef MC_DEBUG
+ printf("\nEntering MC_StartGame()");
+ #endif
+
+ /* if math_opts not set up yet, initialize it: */
+ if (!initialized)
+ {
+
+ #ifdef MC_DEBUG
+ printf("\nNot initialized - calling MC_Initialize()");
+ #endif
+
+ MC_Initialize();
+ }
+
+ if (!math_opts)
+ {
+ #ifdef MC_DEBUG
+ printf("\nCould not initialize - bailing out");
+ printf("\nLeavinging MC_StartGame()\n");
+ #endif
+
+ return 0;
+ }
+ /* we know math_opts exists if we make it to here */
+
+ /* clear out old lists if starting another game: (if not done already) */
+ delete_list(question_list);
+ delete_list(wrong_quests);
+
+ /* set up new list with pointer to top: */
+ 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)
+ {
+ #ifdef MC_DEBUG
+ printf("\nGame set up successfully");
+ printf("\nLeaving MC_StartGame()\n");
+ #endif
+
+ return 1;
+ }
+ else
+ {
+ #ifdef MC_DEBUG
+ printf("\nGame NOT set up successfully - no valid list");
+ printf("\nLeaving MC_StartGame()\n");
+ #endif
+
+ 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)
+{
+ #ifdef MC_DEBUG
+ printf("\nEntering MC_StartGameUsingWrongs()");
+ #endif
+
+ /* 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))
+ {
+ #ifdef MC_DEBUG
+ printf("\nNon-zero length wrong_quests list found, will");
+ printf("\nuse for new game list:");
+ #endif
+
+ /* initialize lists for new game: */
+ delete_list(question_list);
+ question_list = randomize_list(wrong_quests);
+ wrong_quests = 0;
+ next_wrong_quest = 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(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 */
+ {
+ #ifdef MC_DEBUG
+ printf("\nNo wrong questions to review!\n");
+ printf("\nLeaving MC_StartGameUsingWrongs()\n");
+ #endif
+
+ 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)
+{
+ #ifdef MC_DEBUG
+ printf("\nEntering MC_NextQuestion()");
+ #endif
+
+ /* (so we can free the node after removed from list:) */
+ MC_MathQuestion* ptr;
+ ptr = question_list;
+
+ if (!fc )
+ {
+ fprintf(stderr, "\nInvalid MC_FlashCard* argument!\n");
+
+ #ifdef MC_DEBUG
+ printf("\nInvalid MC_FlashCard* argument!");
+ printf("\nLeaving MC_NextQuestion()\n");
+ #endif
+
+ return 0;
+ }
+
+ if (!question_list ||
+/* !next_question || */
+ !list_length(question_list) )
+ {
+ #ifdef MC_DEBUG
+ printf("\nquestion_list invalid or empty");
+ printf("\nLeaving MC_NextQuestion()\n");
+ #endif
+
+ return 0;
+ }
+
+ fc->num1 = question_list->card.num1;
+ fc->num2 = question_list->card.num2;
+ fc->num3 = question_list->card.num3;
+ fc->operation = question_list->card.operation;
+ fc->format = question_list->card.format;
+
+ /* take first question node out of list and free it: */
+ question_list = remove_node(question_list, question_list);
+ free(ptr);
+ quest_list_length--;
+ questions_pending++;
+
+ #ifdef MC_DEBUG
+ printf("\nnext question is:");
+ print_card(*fc);
+ print_counters();
+ printf("\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)
+{
+ #ifdef MC_DEBUG
+ printf("\nEntering MC_AnsweredCorrectly()");
+ #endif
+
+ if (!fc)
+ {
+ fprintf(stderr, "\nMC_AnsweredCorrectly() passed invalid pointer as argument!\n");
+
+ #ifdef MC_DEBUG
+ printf("\nInvalid MC_FlashCard* argument!");
+ printf("\nLeaving MC_AnsweredCorrectly()\n");
+ #endif
+
+ return 0;
+ }
+
+ #ifdef MC_DEBUG
+ printf("\nQuestion was:");
+ print_card(*fc);
+ #endif
+
+ answered_correctly++;
+questions_pending--;
+
+ if (math_opts->recycle_corrects)
+ /* reinsert question into question list at random location */
+ {
+ #ifdef MC_DEBUG
+ printf("\nReinserting question into list");
+ #endif
+
+ 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
+ {
+ #ifdef MC_DEBUG
+ printf("\nNot reinserting question into list");
+ #endif
+ /* not recycling questions so fewer questions remain: */
+ unanswered--;
+ }
+
+ #ifdef MC_DEBUG
+ print_counters();
+ printf("\nLeaving MC_AnsweredCorrectly()\n");
+ #endif
+
+ return 1;
+}
+
+/* MC_AnsweredIncorrectly() is how the user interface */
+/* tells MathCards that the question has been answered */
+/* incorrectly. Returns 1 if no errors. */
+int MC_AnsweredIncorrectly(MC_FlashCard* fc)
+{
+ #ifdef MC_DEBUG
+ printf("\nEntering MC_AnsweredIncorrectly()");
+ #endif
+
+ if (!fc)
+ {
+ fprintf(stderr, "\nMC_AnsweredIncorrectly() passed invalid pointer as argument!\n");
+
+ #ifdef MC_DEBUG
+ printf("\nInvalid MC_FlashCard* argument!");
+ printf("\nLeaving MC_AnsweredIncorrectly()\n");
+ #endif
+
+ 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 */
+ {
+ #ifdef MC_DEBUG
+ printf("\nAdding to wrong_quests list");
+ #endif
+
+ 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->recycle_wrongs)
+ {
+ int i;
+
+ #ifdef MC_DEBUG
+ printf("\nAdding %d copies to question_list:", math_opts->copies_recycled_wrongs);
+ #endif
+
+ /* can put in more than one copy (to drive the point home!) */
+ for (i = 0; i < math_opts->copies_recycled_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->copies_recycled_wrongs - 1);
+ }
+ else
+ {
+ #ifdef MC_DEBUG
+ printf("\nnot recycling wrong answers\n");
+ #endif
+
+ /* not recycling questions so list gets shorter: */
+ unanswered--;
+ }
+
+ #ifdef MC_DEBUG
+ print_counters();
+ printf("\nLeaving MC_Answered_Incorrectly()\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->recycle_wrongs
+ && !unanswered)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* Returns 1 if no more questions left (either in list */
+/* or "in play" */
+int MC_GameOver(void)
+{
+ return !unanswered;
+}
+
+/* Returns 1 if no more questions left in list, NOT */
+/* including questions currently "in play". */
+int MC_NoQuestionsLeft(void)
+{
+ return !quest_list_length;
+}
+
+
+/* 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;
+ }
+
+ initialized = 0;
+}
+
+
+/* Simple Get()- and Set()-style functions for math options settings: */
+
+
+/* Set general math options: */
+void MC_SetMaxAnswer(int max)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetMaxAnswer(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_answer = sane_value(max);
+}
+
+
+void MC_SetAllowNegAnswer(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetAllowNegAnswer(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->allow_neg_answer = int_to_bool(opt);
+}
+
+
+void MC_SetRecycleCorrects(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetRecycleCorrects(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->recycle_corrects = int_to_bool(opt);
+}
+
+
+void MC_SetRecycleWrongs(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetRecycleWrongs(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->recycle_wrongs = int_to_bool(opt);
+}
+
+
+void MC_SetCopiesRecycledWrongs(int copies)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetCopiesRecycledWrongs(): math_opts not valid!\n");
+ return;
+ }
+ /* number of copies must be between 1 and 10: */
+ if (copies < 1)
+ copies = 1;
+ if (copies > 10)
+ copies = 10;
+ math_opts->copies_recycled_wrongs = copies;
+}
+
+/*NOTE - list can contain more than one format */
+void MC_SetFormatAnswerLast(int opt) /* Enable questions like: a + b = ? */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetFormatAnswerLast(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->format_answer_last = int_to_bool(opt);
+}
+
+
+void MC_SetFormatAnswerFirst(int opt) /* Enable questions like: ? + b = c */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetFormatAnswerFirst(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->format_answer_first = int_to_bool(opt);
+}
+
+
+void MC_SetFormatAnswerMiddle(int opt) /* Enable questions like: a + ? = c */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetFormatAnswerMiddle(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->format_answer_middle = int_to_bool(opt);
+}
+
+
+void MC_SetQuestionCopies(int copies) /* how many times each question is put in list */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetQuestionCopies(): math_opts not valid!\n");
+ return;
+ }
+ /* number of copies must be between 1 and 10: */
+ if (copies < 1)
+ copies = 1;
+ if (copies > 10)
+ copies = 10;
+ math_opts->question_copies = copies;
+}
+
+
+void MC_SetRandomize(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetRandomize(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->randomize = int_to_bool(opt);
+}
+
+
+/* Set math operations to be used in game: */
+void MC_SetAddAllowed(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetAddAllowed(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->addition_allowed = int_to_bool(opt);
+}
+
+
+void MC_SetSubAllowed(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetSubAllowed(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->subtraction_allowed = int_to_bool(opt);
+}
+
+
+void MC_SetMultAllowed(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetMultAllowed(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->multiplication_allowed = int_to_bool(opt);
+}
+
+
+void MC_SetDivAllowed(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetDivAllowed(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->division_allowed = int_to_bool(opt);
+}
+
+
+
+
+
+/* Set min and max for addition: */
+void MC_SetAddMin(int opt)
+{
+ MC_SetAddMinAugend(opt);
+ MC_SetAddMinAddend(opt);
+}
+
+
+void MC_SetAddMinAugend(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetAddMinAugend(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->min_augend = sane_value(opt);
+}
+
+
+void MC_SetAddMinAddend(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetAddMinAddend(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->min_addend = sane_value(opt);
+}
+
+
+void MC_SetAddMax(int opt)
+{
+ MC_SetAddMaxAugend(opt);
+ MC_SetAddMaxAddend(opt);
+}
+
+
+void MC_SetAddMaxAugend(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetAddMaxAugend(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_augend = sane_value(opt);
+}
+
+
+void MC_SetAddMaxAddend(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetAddMaxAddend(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_addend = sane_value(opt);
+}
+
+
+
+
+/* Set min and max for subtraction: */
+void MC_SetSubMin(int opt)
+{
+ MC_SetSubMinMinuend(opt);
+ MC_SetSubMinSubtrahend(opt);
+}
+
+
+void MC_SetSubMinMinuend(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_MC_SetSubMinMinuend(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->min_minuend = sane_value(opt);
+}
+
+
+void MC_SetSubMinSubtrahend(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetSubMinSubtrahend(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->min_subtrahend = sane_value(opt);
+}
+
+
+void MC_SetSubMax(int opt)
+{
+ MC_SetSubMaxMinuend(opt);
+ MC_SetSubMaxSubtrahend(opt);
+}
+
+
+void MC_SetSubMaxMinuend(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetSubMaxMinuend(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_minuend = sane_value(opt);
+}
+
+
+void MC_SetSubMaxSubtrahend(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetSubMaxSubtrahend(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_subtrahend = sane_value(opt);
+}
+
+
+
+
+/* Set min and max for multiplication: */
+void MC_SetMultMin(int opt)
+{
+ MC_SetMultMinMultiplier(opt);
+ MC_SetMultMinMultiplicand(opt);
+}
+
+
+void MC_SetMultMinMultiplier(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetMultMinMultiplier(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->min_multiplier = sane_value(opt);
+}
+
+
+void MC_SetMultMinMultiplicand(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetMultMinMultiplicand(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->min_multiplicand = sane_value(opt);
+}
+
+
+void MC_SetMultMax(int opt)
+{
+ MC_SetMultMaxMultiplier(opt);
+ MC_SetMultMaxMultiplicand(opt);
+}
+
+
+void MC_SetMultMaxMultiplier(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetMultMaxMultiplier(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_multiplier = sane_value(opt);
+}
+
+
+void MC_SetMultMaxMultiplicand(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetMultMaxMultiplicand(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_multiplicand = sane_value(opt);
+}
+
+
+
+
+/* Set min and max for division: */
+void MC_SetDivMin(int opt)
+{
+ MC_SetDivMinDivisor(opt);
+ MC_SetDivMinQuotient(opt);
+}
+
+
+void MC_SetDivMinDivisor(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetDivMinDivisor(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->min_divisor = sane_value(opt);
+}
+
+
+void MC_SetDivMinQuotient(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetDivMinQuotient(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->min_quotient = sane_value(opt);
+}
+
+
+void MC_SetDivMax(int opt)
+{
+ MC_SetDivMaxDivisor(opt);
+ MC_SetDivMaxQuotient(opt);
+}
+
+
+void MC_SetDivMaxDivisor(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetDivMaxDivisor(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_divisor = sane_value(opt);
+}
+
+
+void MC_SetDivMaxQuotient(int opt)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SetDivMaxQuotient(): math_opts not valid!\n");
+ return;
+ }
+ math_opts->max_quotient = sane_value(opt);
+}
+
+
+
+
+/*"Get" type methods to query option parameters */
+
+/* Query general math options: */
+int MC_MaxAnswer(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_MaxAnswer(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_answer;
+}
+
+int MC_AllowNegAnswer(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_AllowNegAnswer(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->allow_neg_answer;
+}
+
+
+int MC_RecycleCorrects(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_RecycleCorrects(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->recycle_corrects;
+}
+
+
+int MC_RecycleWrongs(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_RecycleWrongs(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->recycle_wrongs;
+}
+
+
+int MC_CopiesRecycledWrongs(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_CopiesRecycledWrongs(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->copies_recycled_wrongs;
+}
+
+
+int MC_FormatAnswerLast(void) /* a + b = ? */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_FormatAnswerLast(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->format_answer_last;
+}
+
+
+int MC_FormatAnswerFirst(void) /* ? + b = c NOTE - list can contain more than one format */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_FormatAnswerFirst(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->format_answer_first;
+}
+
+
+int MC_FormatAnswerMiddle(void) /* a + ? = c */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_FormatAnswerMiddle(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->format_answer_middle;
+}
+
+
+int MC_QuestionCopies(void) /* how many times each question is put in list */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_QuestionCopies(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->question_copies;
+}
+
+
+int MC_Randomize(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_Randomize(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->randomize;
+}
+
+
+
+/* Query the allowed math operations: */
+int MC_AddAllowed(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_AddAllowed(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->addition_allowed;
+}
+
+
+int MC_SubAllowed(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SubAllowed(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->subtraction_allowed;
+}
+
+
+int MC_MultAllowed(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_MultAllowed(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->multiplication_allowed;
+}
+
+
+int MC_DivAllowed(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_DivAllowed(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->division_allowed;
+}
+
+
+
+/* Query min and max for addition: */
+int MC_AddMinAugend(void) /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_AddMinAugend(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->min_augend;
+}
+
+
+int MC_AddMinAddend(void) /* options for the other addend */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_AddMinAddend(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->min_addend;
+}
+
+
+int MC_AddMaxAugend(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_AddMaxAugend(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_augend;
+}
+
+
+int MC_AddMaxAddend(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_AddMaxAddend(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_addend;
+}
+
+
+
+/* Query min and max for subtraction: */
+int MC_SubMinMinuend(void) /* minuend - subtrahend = difference */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SubMinMinuend(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->min_minuend;
+}
+
+
+int MC_SubMinSubtrahend(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SubMinSubtrahend(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->min_subtrahend;
+}
+
+
+
+int MC_SubMaxMinuend(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SubMaxMinuend(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_minuend;
+}
+
+
+
+int MC_SubMaxSubtrahend(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_SubMaxSubtrahend(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_subtrahend;
+}
+
+
+
+/* Query min and max for multiplication: */
+int MC_MultMinMultiplier(void) /* multiplier * multiplicand = product */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_MultMinMultiplier(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->min_multiplier;
+}
+
+
+int MC_MultMinMultiplicand(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_MultMinMultiplicand(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->min_multiplicand;
+}
+
+
+
+int MC_MultMaxMultiplier(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_MultMaxMultiplier(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_multiplier;
+}
+
+
+
+int MC_MultMaxMultiplicand(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_MultMaxMultiplicand(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_multiplicand;
+}
+
+
+
+/* Query min and max for division: */
+int MC_DivMinDivisor(void) /* dividend/divisor = quotient */
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_DivMinDivisor(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->min_divisor;
+}
+
+
+int MC_DivMinQuotient(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_DivMinQuotient(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->min_quotient;
+}
+
+
+int MC_DivMaxDivisor(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_DivMaxDivisor(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_divisor;
+}
+
+
+int MC_DivMaxQuotient(void)
+{
+ if (!math_opts)
+ {
+ fprintf(stderr, "\nMC_DivMaxQuotient(): math_opts not valid!\n");
+ return MC_MATH_OPTS_INVALID;
+ }
+ return math_opts->max_quotient;
+}
+
+
+
+/* Implementation of "private methods" - (cannot be called from outside
+of this file) */
+
+
+/* using parameters from the mission struct, create linked list of "flashcards" */
+/* FIXME should figure out how to proceed correctly if we run out of memory */
+/* FIXME implement question formats and question_copies flags */
+/* FIXME handle max_answer correctly, even if negative */
+MC_MathQuestion* generate_list(void)
+{
+ MC_MathQuestion* top_of_list = 0;
+ MC_MathQuestion* end_of_list = 0;
+ MC_MathQuestion* tmp_ptr = 0;
+
+ int i, j;
+ int length = 0;
+
+ #ifdef MC_DEBUG
+ printf("\nEntering generate_list()");
+ print_math_options();
+ #endif
+
+ /* add nodes for each math operation allowed */
+ if (math_opts->addition_allowed)
+ {
+ for (i = math_opts->min_augend; i <= math_opts->max_augend; i++)
+ {
+ for (j = math_opts->min_addend; j <= math_opts->max_addend; j++)
+ {
+ /* prevent negative numbers if desired */
+ if (math_opts->allow_neg_answer || ((i >= 0) && (j >=0)))
+ {
+ /* make sure max_questions not exceeded */
+ if (length < math_opts->max_questions)
+ {
+ tmp_ptr = create_node(i, j, MC_OPER_ADD, i + j, MC_FORMAT_ANS_LAST);
+ top_of_list = insert_node(top_of_list, end_of_list, tmp_ptr);
+ end_of_list = tmp_ptr;
+ length++;
+ }
+ }
+ }
+ }
+ }
+
+ if (math_opts->subtraction_allowed)
+ {
+ for (i = math_opts->min_minuend; i <= math_opts->max_minuend; i++)
+ {
+ for (j = math_opts->min_subtrahend; j <= math_opts->max_subtrahend; j++)
+ {
+ /* prevent negative numbers if desired */
+ if (math_opts->allow_neg_answer || (i >= j))
+ {
+ /* make sure max_questions not exceeded */
+ if (length < math_opts->max_questions)
+ {
+ tmp_ptr = create_node(i, j, MC_OPER_SUB, i - j, MC_FORMAT_ANS_LAST);
+ top_of_list = insert_node(top_of_list, end_of_list, tmp_ptr);
+ end_of_list = tmp_ptr;
+ length++;
+ }
+ }
+ }
+ }
+ }
+
+ if (math_opts->multiplication_allowed)
+ {
+ for (i = math_opts->min_multiplier; i <= math_opts->max_multiplier; i++)
+ {
+ for (j = math_opts->min_multiplicand; j <= math_opts->max_multiplicand; j++)
+ {
+ /* prevent negative numbers if desired */
+ if (math_opts->allow_neg_answer || ((i >= 0) && (j >=0)))
+ {
+ /* make sure max_questions not exceeded */
+ if (length < math_opts->max_questions)
+ {
+ tmp_ptr = create_node(i, j, MC_OPER_MULT, i * j, MC_FORMAT_ANS_LAST);
+ top_of_list = insert_node(top_of_list, end_of_list, tmp_ptr);
+ end_of_list = tmp_ptr;
+ length++;
+ }
+ }
+ }
+ }
+ }
+
+ if (math_opts->division_allowed)
+ {
+ for (i = math_opts->min_quotient; i <= math_opts->max_quotient; i++)
+ {
+ for (j = math_opts->min_divisor; j <= math_opts->max_divisor; j++)
+ {
+ /* prevent negative numbers if desired */
+ if (math_opts->allow_neg_answer || ((i >= 0) && (j >=0)))
+ {
+ if (j) /* prevent division by zero */
+ {
+ /* make sure max_questions not exceeded */
+ if (length < math_opts->max_questions)
+ {
+ tmp_ptr = create_node(i * j, j, MC_OPER_DIV, i, MC_FORMAT_ANS_LAST);
+ top_of_list = insert_node(top_of_list, end_of_list, tmp_ptr);
+ end_of_list = tmp_ptr;
+ length++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #ifdef MC_DEBUG
+ length = list_length(top_of_list);
+ printf("\nlength before randomization:\t%d", length);
+ #endif
+
+ /* now shuffle list if desired: */
+ if (math_opts->randomize)
+ {
+ top_of_list = randomize_list(top_of_list);
+ }
+
+ #ifdef MC_DEBUG
+ length = list_length(top_of_list);
+ printf("\nlength after randomization:\t%d", length);
+ printf("\nLeaving generate_list()\n");
+ #endif
+
+ return top_of_list;
+}
+
+
+
+
+/* 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;
+ ptr = malloc(sizeof(MC_MathQuestion));
+ ptr->card.num1 = n1;
+ ptr->card.num2 = n2;
+ ptr->card.num3 = ans;
+ ptr->card.operation = op;
+ ptr->card.format = f;
+ ptr->next = 0;
+ ptr->previous =0;
+ return ptr;
+}
+
+
+
+/* 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* ptr;
+ if (!other)
+ return 0;
+ ptr = malloc(sizeof(MC_MathQuestion));
+ ptr->card.num1 = other->card.num1;
+ ptr->card.num2 = other->card.num2;
+ ptr->card.num3 = other->card.num3;
+ ptr->card.operation = other->card.operation;
+ ptr->card.format = other->card.format;
+ ptr->next = 0;
+ ptr->previous = 0;
+ return ptr;
+}
+
+
+
+MC_MathQuestion* create_node_from_card(MC_FlashCard* flashcard)
+{
+ MC_MathQuestion* ptr;
+ if (!flashcard)
+ return 0;
+ ptr = malloc(sizeof(MC_MathQuestion));
+ ptr->card.num1 = flashcard->num1;
+ ptr->card.num2 = flashcard->num2;
+ ptr->card.num3 = flashcard->num3;
+ ptr->card.operation = flashcard->operation;
+ ptr->card.format = flashcard->format;
+ ptr->next = 0;
+ ptr->previous = 0;
+ return ptr;
+}
+
+
+
+MC_FlashCard* create_card_from_node(MC_MathQuestion* node)
+{
+ MC_FlashCard* fc;
+ if (!node)
+ return 0;
+ fc = malloc(sizeof(MC_FlashCard));
+ fc->num1 = node->card.num1;
+ fc->num2 = node->card.num2;
+ fc->num3 = node->card.num3;
+ fc->operation = node->card.operation;
+ fc->format = node->card.format;
+ return fc;
+}
+
+
+
+/* this one copies the contents, including pointers; both nodes must be allocated */
+int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy)
+{
+ if (!original || !copy)
+ {
+ printf("\nIn copy_node(): invalid pointer as argument.\n");
+ fprintf(stderr, "\nIn copy_node(): invalid pointer as argument.\n");
+ return 0;
+ }
+ copy->card.num1 = original->card.num1;
+ copy->card.num2 = original->card.num2;
+ copy->card.num3 = original->card.num3;
+ copy->card.operation = original->card.operation;
+ copy->card.format = original->card.format;
+ copy->next = original->next;
+ copy->previous = original->previous;
+ 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 (list);
+ list = tmp_ptr;
+ }
+ return list;
+}
+
+
+
+/* prints struct to stdout for testing purposes */
+void print_math_options(void)
+{
+ printf("\nprint_math_options():\n");
+
+ /* bail out if no struct */
+ if (!math_opts)
+ {
+ printf("\nMath Options struct does not exist!\n");
+ return;
+ }
+
+ printf("\nGeneral math options:\n");
+ printf("allow_neg_answer:\t%d\n", math_opts->allow_neg_answer);
+ printf("max_answer:\t%d\n", math_opts->max_answer);
+ printf("max_questions:\t%d\n", math_opts->max_questions);
+ printf("format_answer_last:\t%d\n", math_opts->format_answer_last);
+ printf("format_answer_first:\t%d\n", math_opts->format_answer_first);
+ printf("format_answer_middle:\t%d\n", math_opts->format_answer_middle);
+ printf("question_copies:\t%d\n", math_opts->question_copies);
+ printf("randomize:\t%d\n", math_opts->randomize);
+
+ printf("\nSpecific math operation options:\n");
+ printf("addition_allowed:\t%d\n", math_opts->addition_allowed);
+ printf("min_augend:\t%d\n", math_opts->min_augend);
+ printf("max_augend:\t%d\n", math_opts->max_augend);
+ printf("min_addend:\t%d\n", math_opts->min_addend);
+ printf("max_addend:\t%d\n", math_opts->max_addend);
+
+ printf("subtraction_allowed\t%d\n", math_opts->subtraction_allowed);
+ printf("min_minuend:\t%d\n", math_opts->min_minuend);
+ printf("max_minuend:\t%d\n", math_opts->max_minuend);
+ printf("min_subtrahend:\t%d\n", math_opts->min_subtrahend);
+ printf("max_subtrahend:\t%d\n", math_opts->max_subtrahend);
+
+ printf("multiplication_allowed:\t%d\n", math_opts->multiplication_allowed);
+ printf("min_multiplier:\t%d\n", math_opts->min_multiplier);
+ printf("max_multiplier:\t%d\n", math_opts->max_multiplier);
+ printf("min_multiplicand:\t%d\n", math_opts->min_multiplicand);
+ printf("max_multiplicand:\t%d\n", math_opts->max_multiplicand);
+
+ printf("division_allowed:\t%d\n", math_opts->division_allowed);
+ printf("min_divisor:\t%d\n",math_opts->min_divisor);
+ printf("max_divisor:\t%d\n", math_opts->max_divisor);
+ printf("min_quotient:\t%d\n", math_opts->min_quotient);
+ printf("max_quotient:\t%d\n", math_opts->max_quotient);
+}
+
+
+
+void print_list(MC_MathQuestion* list)
+{
+ if (!list)
+ {
+ printf("\nprint_list(): list empty or pointer invalid\n");
+ return;
+ }
+ MC_MathQuestion* ptr = list;
+ printf("\nprint_list() printing list:");
+ printf("\nlist_length():\t%d", list_length(list));
+ while (ptr)
+ {
+ print_node(ptr);
+ ptr = ptr->next;
+ }
+}
+
+
+
+void print_node(MC_MathQuestion* ptr)
+{
+ if (ptr)
+ printf("\n%d, %d \tOper %d \tAnswer %d",
+ ptr->card.num1,
+ ptr->card.num2,
+ ptr->card.operation,
+ ptr->card.num3);
+}
+
+
+
+void print_card(MC_FlashCard card)
+{
+ printf("\nprint_card():");
+ printf("\n%d, %d \tOper %d \tAnswer %d \t Format %d\n",
+ card.num1,
+ card.num2,
+ card.operation,
+ card.num3,
+ card.format);
+}
+
+
+
+
+/* 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);
+}
+
+int list_length(MC_MathQuestion* list)
+{
+ int length = 0;
+ while (list)
+ {
+ length++;
+ list = list->next;
+ }
+ return length;
+}
+
+
+
+
+MC_MathQuestion* randomize_list(MC_MathQuestion* old_list)
+{
+
+ MC_MathQuestion* old_tmp = 0;
+ MC_MathQuestion* new_list = 0;
+ MC_MathQuestion* new_tmp =0;
+
+ int old_length = list_length(old_list);
+ int new_length = 0;
+
+ #ifdef MC_DEBUG
+ printf("\nEntering randomize_list()");
+ printf("\nBefore randomization:");
+ printf("\nPrinting old_list:");
+ print_list(old_list);
+ printf("\nPrinting new_list:");
+ print_list(new_list);
+ #endif
+
+
+ while (old_length && old_list)
+ {
+ old_tmp = pick_random(old_length, old_list);
+ new_tmp = pick_random(new_length, new_list);
+
+ if (old_tmp)
+ {
+ old_list = remove_node(old_list, old_tmp);
+ new_list = insert_node(new_list, new_tmp, old_tmp);
+ old_length--;
+ new_length++;
+ }
+ else
+ {
+ #ifdef MC_DEBUG
+ printf("\nUnexpected exit!");
+ printf("\nAfter randomization:");
+ printf("\nPrinting old_list:");
+ print_list(old_list);
+ printf("\nPrinting new_list:");
+ print_list(new_list);
+ printf("\nLeaving randomize_list()");
+ #endif
+
+ return new_list;
+ }
+ }
+
+ #ifdef MC_DEBUG
+ printf("\nAfter randomization:");
+ printf("\nPrinting old_list:");
+ print_list(old_list);
+ printf("\nPrinting new_list:");
+ print_list(new_list);
+ printf("\nLeaving randomize_list()");
+ #endif
+
+ return new_list;
+}
+
+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 (first->card.num1 == other->card.num1
+ && first->card.num2 == other->card.num2
+ && first->card.operation == other->card.operation
+ && first->card.format == other->card.format)
+ 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->allow_neg_answer)
+ {
+ i = 0;
+ }
+
+ return i;
+}
+
+int abs_value(int i)
+{
+ if (i > 0)
+ return i;
+ else
+ return -i;
+}
Added: tuxmath/trunk/src/mathcards.h
===================================================================
--- tuxmath/trunk/src/mathcards.h 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/mathcards.h 2006-05-16 17:05:20 UTC (rev 10)
@@ -0,0 +1,313 @@
+/*
+
+ 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
+
+#undef MC_DEBUG
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* type of math operation used in a given question */
+enum {
+ MC_OPER_ADD,
+ MC_OPER_SUB,
+ MC_OPER_MULT,
+ MC_OPER_DIV,
+ MC_NUM_OPERS
+};
+
+/* math question formats: */
+enum {
+ MC_FORMAT_ANS_LAST, /* a + b = ? */
+ MC_FORMAT_ANS_FIRST, /* ? + b = c */
+ MC_FORMAT_ANS_MIDDLE /* a + ? = c */
+};
+
+/* This struct contains all options that determine what */
+/* math questions are asked during a game */
+typedef struct MC_Options {
+ /* general math options */
+ int allow_neg_answer;
+ int max_answer;
+ int max_questions;
+ int recycle_corrects;
+ int recycle_wrongs;
+ int copies_recycled_wrongs;
+ int format_answer_last; /* a + b = ? */
+ int format_answer_first; /* ? + b = c NOTE - list can contain more than one format */
+ int format_answer_middle; /* a + ? = c */
+ int question_copies; /* how many times each question is put in list */
+ int randomize; /* whether to shuffle cards */
+ /* addition options */
+ int addition_allowed;
+ int min_augend; /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+ int max_augend;
+ int min_addend; /* options for the other addend */
+ int max_addend;
+ /* subtraction options */
+ int subtraction_allowed;
+ int min_minuend; /* minuend - subtrahend = difference */
+ int max_minuend;
+ int min_subtrahend;
+ int max_subtrahend;
+ /* multiplication options */
+ int multiplication_allowed;
+ int min_multiplier; /* multiplier * multiplicand = product */
+ int max_multiplier;
+ int min_multiplicand;
+ int max_multiplicand;
+ /* division options */
+ int division_allowed;
+ int min_divisor; /* dividend/divisor = quotient */
+ int max_divisor;
+ int min_quotient;
+ int max_quotient;
+} MC_Options;
+
+/* default values for math_options */
+#define MC_GLOBAL_MAX 999 /* this is the largest absolute value that can be entered */
+ /* as a max or min for math question values. */
+#define MC_MATH_OPTS_INVALID -9999 /* return value for accessor functions if math_opts not valid */
+
+#define DEFAULT_ALLOW_NEG_ANSWER 0
+#define DEFAULT_MAX_ANSWER 144
+#define DEFAULT_MAX_QUESTIONS 5000
+#define DEFAULT_RECYCLE_CORRECTS 1 /* reuse correctly answered questions or not */
+#define DEFAULT_RECYCLE_WRONGS 1 /* reuse incorrectly answered questions or not */
+#define DEFAULT_COPIES_RECYCLED_WRONGS 1 /* how many copies of an incorrectly answered question to re-insert*/
+#define DEFAULT_FORMAT_ANSWER_LAST 1 /* question format is: a + b = ? */
+#define DEFAULT_FORMAT_ANSWER_FIRST 0 /* question format is: ? + b = c */
+#define DEFAULT_FORMAT_ANSWER_MIDDLE 0 /* question format is: a + ? = c */
+#define DEFAULT_QUESTION_COPIES 1 /* how many times each question is put in list */
+#define DEFAULT_RANDOMIZE 1 /* whether to shuffle cards */
+
+#define DEFAULT_ADDITION_ALLOWED 1
+#define DEFAULT_MIN_AUGEND 0 /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+#define DEFAULT_MAX_AUGEND 12
+#define DEFAULT_MIN_ADDEND 0
+#define DEFAULT_MAX_ADDEND 12
+
+#define DEFAULT_SUBTRACTION_ALLOWED 1 /* minuend - subtrahend = difference */
+#define DEFAULT_MIN_MINUEND 0
+#define DEFAULT_MAX_MINUEND 12
+#define DEFAULT_MIN_SUBTRAHEND 0
+#define DEFAULT_MAX_SUBTRAHEND 12
+
+#define DEFAULT_MULTIPLICATION_ALLOWED 1
+#define DEFAULT_MIN_MULTIPLIER 0 /* multiplier * multiplicand = product */
+#define DEFAULT_MAX_MULTIPLIER 12
+#define DEFAULT_MIN_MULTIPLICAND 0
+#define DEFAULT_MAX_MULTIPLICAND 12
+
+#define DEFAULT_DIVISION_ALLOWED 1 /* dividend/divisor = quotient */
+#define DEFAULT_MIN_DIVISOR 0 /* note - generate_list() will prevent */
+#define DEFAULT_MAX_DIVISOR 12 /* questions with division by zero. */
+#define DEFAULT_MIN_QUOTIENT 0
+#define DEFAULT_MAX_QUOTIENT 12
+
+/* struct for individual "flashcard" */
+typedef struct MC_FlashCard {
+ int num1;
+ int num2;
+ int num3;
+ int operation;
+ int format;
+} MC_FlashCard;
+
+/* struct for node in math "flashcard" list */
+typedef struct MC_MathQuestion {
+ MC_FlashCard card;
+ struct MC_MathQuestion *next;
+ struct MC_MathQuestion *previous;
+} 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);
+
+/* MC_AnsweredIncorrectly() is how the user interface */
+/* tells MathCards that the question has been answered */
+/* incorrectly. Returns 1 if no errors. */
+int MC_AnsweredIncorrectly(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 1 if no more questions left (either in list */
+/* or "in play" */
+/* FIXME would be better to return number of questions */
+/* left so it can be displayed in a counter. */
+int MC_GameOver(void);
+
+/* Returns 1 if no more questions left in list, NOT */
+/* including questions currently "in play". */
+int MC_NoQuestionsLeft(void);
+
+/* Tells MathCards to clean up - should be called when */
+/* user interface program exits. */
+void MC_EndGame(void);
+
+
+
+/* Simple "Set/Get" type functions for option parameters: */
+
+/* Simple functions to set option parameters: */
+
+/* Set general math options: */
+void MC_SetMaxAnswer(int max);
+void MC_SetAllowNegAnswer(int opt);
+void MC_SetRecycleCorrects(int opt);
+void MC_SetRecycleWrongs(int opt);
+void MC_SetCopiesRecycledWrongs(int copies);
+void MC_SetFormatAnswerLast(int opt); /* a + b = ? */
+void MC_SetFormatAnswerFirst(int opt); /* ? + b = c NOTE - list can contain more than one format */
+void MC_SetFormatAnswerMiddle(int opt); /* a + ? = c */
+void MC_SetQuestionCopies(int copies); /* how many times each question is put in list */
+void MC_SetRandomize(int opt);
+
+/* Set the allowed math operations: */
+void MC_SetAddAllowed(int opt);
+void MC_SetSubAllowed(int opt);
+void MC_SetMultAllowed(int opt);
+void MC_SetDivAllowed(int opt);
+
+/* Set min and max for addition: */
+void MC_SetAddMin(int opt); /* augend + addend = sum */
+void MC_SetAddMinAugend(int opt); /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+void MC_SetAddMinAddend(int opt); /* options for the other addend */
+void MC_SetAddMax(int opt);
+void MC_SetAddMaxAugend(int opt);
+void MC_SetAddMaxAddend(int opt);
+
+/* Set min and max for subtraction: */
+void MC_SetSubMin(int opt);
+void MC_SetSubMinMinuend(int opt); /* minuend - subtrahend = difference */
+void MC_SetSubMinSubtrahend(int opt);
+void MC_SetSubMax(int opt);
+void MC_SetSubMaxMinuend(int opt);
+void MC_SetSubMaxSubtrahend(int opt);
+
+/* Set min and max for multiplication: */
+void MC_SetMultMin(int opt);
+void MC_SetMultMinMultiplier(int opt); /* multiplier * multiplicand = product */
+void MC_SetMultMinMultiplicand(int opt);
+void MC_SetMultMax(int opt);
+void MC_SetMultMaxMultiplier(int opt);
+void MC_SetMultMaxMultiplicand(int opt);
+
+/* Set min and max for division: */
+void MC_SetDivMin(int opt);
+void MC_SetDivMinDivisor(int opt); /* dividend/divisor = quotient */
+void MC_SetDivMinQuotient(int opt);
+void MC_SetDivMax(int opt);
+void MC_SetDivMaxDivisor(int opt);
+void MC_SetDivMaxQuotient(int opt);
+
+
+/* "Get" type functions to query option parameters: */
+
+/* Query general math options: */
+int MC_MaxAnswer(void);
+int MC_AllowNegAnswer(void);
+int MC_RecycleCorrects(void);
+int MC_RecycleWrongs(void);
+int MC_CopiesRecycledWrongs(void);
+int MC_FormatAnswerLast(void); /* a + b = ? */
+int MC_FormatAnswerFirst(void); /* ? + b = c NOTE - list can contain more than one format */
+int MC_FormatAnswerMiddle(void); /* a + ? = c */
+int MC_QuestionCopies(void); /* how many times each question is put in list */
+int MC_Randomize(void);
+
+/* Query the allowed math operations: */
+int MC_AddAllowed(void);
+int MC_SubAllowed(void);
+int MC_MultAllowed(void);
+int MC_DivAllowed(void);
+
+/* Query min and max for addition: */
+int MC_AddMinAugend(void); /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+int MC_AddMinAddend(void); /* options for the other addend */
+int MC_AddMaxAugend(void);
+int MC_AddMaxAddend(void);
+
+/* Query min and max for subtraction: */
+int MC_SubMinMinuend(void); /* minuend - subtrahend = difference */
+int MC_SubMinSubtrahend(void);
+int MC_SubMaxMinuend(void);
+int MC_SubMaxSubtrahend(void);
+
+/* Query min and max for multiplication: */
+int MC_MultMinMultiplier(void); /* multiplier * multiplicand = product */
+int MC_MultMinMultiplicand(void);
+int MC_MultMaxMultiplier(void);
+int MC_MultMaxMultiplicand(void);
+
+/* Query min and max for division: */
+int MC_DivMinDivisor(void); /* dividend/divisor = quotient */
+int MC_DivMinQuotient(void);
+int MC_DivMaxDivisor(void);
+int MC_DivMaxQuotient(void);
+
+#endif
Modified: tuxmath/trunk/src/options.c
===================================================================
--- tuxmath/trunk/src/options.c 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/options.c 2006-05-16 17:05:20 UTC (rev 10)
@@ -22,6 +22,8 @@
#include <SDL.h>
+#include "mathcards.h"
+
#include "options.h"
#include "images.h"
#include "setup.h"
@@ -29,6 +31,7 @@
#include "playsound.h"
#include "game.h"
#include "tuxmath.h"
+
/* FIXME figure out what oper_override is supposed to do and make sure */
/* this file behaves accordingly! */
@@ -55,7 +58,13 @@
dest.y = 0;
SDL_BlitSurface(images[IMG_OPTIONS], NULL, screen, &dest);
+ /* Syncrhonize old opers[] array with selections from MathCards */
+ opers[OPT_OP_ADD] = MC_AddAllowed();
+ opers[OPT_OP_SUB] = MC_SubAllowed();
+ opers[OPT_OP_MUL] = MC_MultAllowed();
+ opers[OPT_OP_DIV] = MC_DivAllowed();
+
/* Draw options: */
for (i = 0; i < NUM_OPTS; i++)
@@ -81,7 +90,7 @@
{
/* Maximum answer: */
- snprintf(tmp_str, sizeof(tmp_str), "%04d", math_options->max_answer);
+ snprintf(tmp_str, sizeof(tmp_str), "%04d", MC_MaxAnswer());
draw_nums(tmp_str,
screen->w - ((images[IMG_NUMS]->w / 14) * 2) - 16,
y + images[IMG_OPT_MAX_ANSWER]->h);
@@ -303,8 +312,34 @@
{
if (option >= OPT_OP_ADD && option < OPT_OP_ADD + NUM_OPERS)
{
+ /* toggle selection of math operation - old opers array */
+ /* FIXME opers[] to go away */
opers[option - OPT_OP_ADD] = !opers[option - OPT_OP_ADD];
-
+ /* toggle selection in new MathCards backend: */
+ switch (option)
+ {
+ case OPT_OP_ADD:
+ {
+ MC_SetAddAllowed(!MC_AddAllowed());
+ break;
+ }
+ case OPT_OP_SUB:
+ {
+ MC_SetSubAllowed(!MC_SubAllowed());
+ break;
+ }
+ case OPT_OP_MUL:
+ {
+ MC_SetMultAllowed(!MC_MultAllowed());
+ break;
+ }
+ case OPT_OP_DIV:
+ {
+ MC_SetDivAllowed(!MC_DivAllowed());
+ break;
+ }
+ }
+
dest.x = screen->w - images[IMG_OPT_CHECK]->w - 16;
dest.y = (images[IMG_OPTIONS]->h + 8 +
((option - OPT_OP_ADD) *
@@ -318,9 +353,9 @@
else if (option == OPT_A_MAX)
{
- math_options->max_answer = (math_options->max_answer * 2) / 3;
- if (math_options->max_answer < 12)
- math_options->max_answer = 144;
+ MC_SetMaxAnswer((MC_MaxAnswer() * 2) / 3);
+ if (MC_MaxAnswer() < 12)
+ MC_SetMaxAnswer(144);
dest.x = screen->w - ((images[IMG_NUMS]->w / 14) * 4) - 16;
dest.y = (images[IMG_OPTIONS]->h + 8 +
@@ -330,7 +365,7 @@
dest.h = images[IMG_OPT_MAX_ANSWER]->h;
SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
- snprintf(tmp_str, sizeof(tmp_str), "%04d", math_options->max_answer);
+ snprintf(tmp_str, sizeof(tmp_str), "%04d", MC_MaxAnswer());
draw_nums(tmp_str,
screen->w - ((images[IMG_NUMS]->w / 14) * 2) - 16,
(images[IMG_OPTIONS]->h + 8 +
@@ -365,6 +400,7 @@
else if (option == OPT_Q_RANGE)
{
+ /* FIXME question ranges now handled in MathCards */
/* Change which ranges are available: */
range_bits = range_bits + 1;
if (range_bits >= (1 << NUM_Q_RANGES))
@@ -399,7 +435,36 @@
+ images[IMG_OPT_RNG_1_5 + j * 2 + range_enabled[j]]-> w
+ 16;
}
+
+ /* update settings in MathCards: */
+ {
+ int lowest_range, highest_range, minimum, maximum;
+ lowest_range = NUM_Q_RANGES - 1;
+ highest_range = 0;
+
+ /* find lowest and highest enabled ranges */
+ for (j = 0; j < NUM_Q_RANGES; j++)
+ {
+ if (range_enabled[j] && j < lowest_range)
+ lowest_range = j;
+ if (range_enabled[j] && j > highest_range)
+ highest_range = j;
+ }
+ minimum = ranges[lowest_range].min;
+ maximum = ranges[highest_range].max;
+
+ /* update MathCards: */
+ MC_SetAddMin(minimum);
+ MC_SetAddMax(maximum);
+ MC_SetSubMin(minimum);
+ MC_SetSubMax(maximum);
+ MC_SetMultMin(minimum);
+ MC_SetMultMax(maximum);
+ MC_SetDivMin(minimum);
+ MC_SetDivMax(maximum);
+ }
}
+
/* same sound for all option updates */
playsound(SND_LASER);
}
Modified: tuxmath/trunk/src/setup.c
===================================================================
--- tuxmath/trunk/src/setup.c 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/setup.c 2006-05-16 17:05:20 UTC (rev 10)
@@ -29,6 +29,8 @@
#endif
#include <SDL_image.h>
+#include "mathcards.h"
+
#include "tuxmath.h"
#include "setup.h"
#include "images.h"
@@ -164,16 +166,14 @@
int opers[NUM_OPERS], range_enabled[NUM_Q_RANGES];
-math_option_type* math_options;
game_option_type* game_options;
/* Local function prototypes: */
void seticon(void);
void usage(int err, char * cmd);
-int initialize_math_options(math_option_type* opts);
+
int initialize_game_options(game_option_type* opts);
-void print_math_options(math_option_type* opts);
void print_game_options(game_option_type* opts);
/* --- Set-up function! --- */
@@ -185,31 +185,29 @@
screen = NULL;
- /* initialize game_options and math_options structs with defaults DSB */
- /* FIXME: Program should load options from disk */
- math_options = malloc(sizeof(math_option_type));
- if (!initialize_math_options(math_options))
+ if (!MC_Initialize())
{
- printf("\nUnable to initialize math_options");
- fprintf(stderr, "\nUnable to initialize math_options");
+ printf("\nUnable to initialize MathCards\n");
+ fprintf(stderr, "\nUnable to initialize MathCards\n");
exit(1);
}
+ /* initialize game_options struct with defaults DSB */
game_options = malloc(sizeof(game_option_type));
if (!initialize_game_options(game_options))
{
- printf("\nUnable to initialize game_options");
- fprintf(stderr, "\nUnable to initialize game_options");
+ printf("\nUnable to initialize game_options\n");
+ fprintf(stderr, "\nUnable to initialize game_options\n");
exit(1);
}
-
-
+/* FIXME will not need this when MathCards used */
+/*
for (i = 0; i < NUM_OPERS; i++)
{
opers[i] = 1;
}
-
+*/
for (i = 0; i < NUM_Q_RANGES; i++)
{
range_enabled[i] = 1;
@@ -339,7 +337,7 @@
else if (strcmp(argv[i], "--allownegatives") == 0 ||
strcmp(argv[i], "-n") == 0)
{
- math_options->allow_neg_answer = 1;
+ MC_SetAllowNegAnswer(1);
}
else if (strcmp(argv[i], "--speed") == 0 ||
strcmp(argv[i], "-s") == 0)
@@ -615,100 +613,18 @@
/* free any heap memory used during game DSB */
void cleanup()
{
- if (math_options)
- free(math_options);
if (game_options)
free(game_options);
+ MC_EndGame();
}
/* Set up math_options struct with defaults from tuxmath.h, */
/* with simple sanity check for negatives */
/* FIXME Should there be more error checking here? */
+/* FIXME move this to mathcards.c */
-int initialize_math_options(math_option_type* opts)
-{
- /* bail out if no struct */
- if (!opts)
- return 0;
- /* set general math options */
- opts->allow_neg_answer = DEFAULT_ALLOW_NEG_ANSWER;
- opts->max_answer = DEFAULT_MAX_ANSWER;
- opts->max_questions = DEFAULT_MAX_QUESTIONS;
- opts->format_answer_last = DEFAULT_FORMAT_ANSWER_LAST;
- opts->format_answer_first = DEFAULT_FORMAT_ANSWER_FIRST;
- opts->format_answer_middle = DEFAULT_FORMAT_ANSWER_MIDDLE;
- opts->question_copies = DEFAULT_QUESTION_COPIES;
- /* set addition options */
- opts->addition_allowed = DEFAULT_ADDITION_ALLOWED;
- opts->min_augend = DEFAULT_MIN_AUGEND;
- opts->max_augend = DEFAULT_MAX_AUGEND;
- opts->min_addend = DEFAULT_MIN_ADDEND;
- opts->max_addend = DEFAULT_MAX_ADDEND;
- /* set subtraction options */
- opts->subtraction_allowed = DEFAULT_SUBTRACTION_ALLOWED;
- opts->min_minuend = DEFAULT_MIN_MINUEND;
- opts->max_minuend = DEFAULT_MAX_MINUEND;
- opts->min_subtrahend = DEFAULT_MIN_SUBTRAHEND;
- opts->max_subtrahend = DEFAULT_MAX_SUBTRAHEND;
- /* set multiplication options */
- opts->multiplication_allowed = DEFAULT_MULTIPLICATION_ALLOWED;
- opts->min_multiplier = DEFAULT_MIN_MULTIPLIER;
- opts->max_multiplier = DEFAULT_MAX_MULTIPLIER;
- opts->min_multiplicand = DEFAULT_MIN_MULTIPLICAND;
- opts->max_multiplicand = DEFAULT_MAX_MULTIPLICAND;
- /* set division options */
- opts->division_allowed = DEFAULT_DIVISION_ALLOWED;
- opts->min_divisor = DEFAULT_MIN_DIVISOR;
- opts->max_divisor = DEFAULT_MAX_DIVISOR;
- opts->min_quotient = DEFAULT_MIN_QUOTIENT;
- opts->max_quotient = DEFAULT_MAX_QUOTIENT;
- /* if no negatives to be used, reset any negatives to 0 */
- if (!opts->allow_neg_answer)
- {
- if (opts->min_augend < 0)
- opts->min_augend = 0;
- if (opts->max_augend < 0)
- opts->max_augend = 0;
- if (opts->min_addend < 0)
- opts->min_addend = 0;
- if (opts->max_addend < 0)
- opts->max_addend = 0;
-
- if (opts->min_minuend < 0)
- opts->min_minuend = 0;
- if (opts->max_minuend < 0)
- opts->max_minuend = 0;
- if (opts->min_subtrahend < 0)
- opts->min_subtrahend = 0;
- if (opts->max_subtrahend < 0)
- opts->max_subtrahend = 0;
-
- if (opts->min_multiplier < 0)
- opts->min_multiplier = 0;
- if (opts->max_multiplier < 0)
- opts->max_multiplier = 0;
- if (opts->min_multiplicand < 0)
- opts->min_multiplicand = 0;
- if (opts->max_multiplicand < 0)
- opts->max_multiplicand = 0;
-
- if (opts->min_divisor < 0)
- opts->min_divisor = 0;
- if (opts->max_divisor < 0)
- opts->max_divisor = 0;
- if (opts->min_quotient < 0)
- opts->min_quotient = 0;
- if (opts->max_quotient < 0)
- opts->max_quotient = 0;
- }
-
- /* for testing purposes */
- /* print_math_options(opts); */
- return 1;
-}
-
int initialize_game_options(game_option_type* opts)
{
/* bail out if no struct */
@@ -724,7 +640,11 @@
opts->use_keypad = DEFAULT_USE_KEYPAD;
opts->speed = DEFAULT_SPEED;
opts->allow_speedup = DEFAULT_ALLOW_SPEEDUP;
+ opts->speedup_factor = DEFAULT_SPEEDUP_FACTOR;
+ opts->max_speed = DEFAULT_MAX_SPEED;
+ opts->slow_after_wrong = DEFAULT_SLOW_AFTER_WRONG;
opts->reuse_questions = DEFAULT_REUSE_QUESTIONS;
+ opts->extra_comets = DEFAULT_EXTRA_COMETS;
opts->max_comets = DEFAULT_MAX_COMETS;
opts->num_cities = DEFAULT_NUM_CITIES; /* MUST BE AN EVEN NUMBER! */
opts->num_bkgds = DEFAULT_NUM_BKGDS;
@@ -735,49 +655,8 @@
return 1;
}
-/* prints struct to stdout for testing purposes */
-void print_math_options(math_option_type* opts)
-{
- /* bail out if no struct */
- if (!opts)
- return;
- printf("\nPrinting members of math_options struct:\n");
- printf("\nGeneral math options:\n");
- printf("allow_neg_answer:\t%d\n", opts->allow_neg_answer);
- printf("max_answer:\t%d\n", opts->max_answer);
- printf("max_questions:\t%d\n", opts->max_questions);
- printf("format_answer_last:\t%d\n", opts->format_answer_last);
- printf("format_answer_first:\t%d\n", opts->format_answer_first);
- printf("format_answer_middle:\t%d\n", opts->format_answer_middle);
- printf("question_copies:\t%d\n", opts->question_copies);
- printf("\nSpecific math operation options:\n");
- printf("addition_allowed:\t%d\n", opts->addition_allowed);
- printf("min_augend:\t%d\n", opts->min_augend);
- printf("max_augend:\t%d\n", opts->max_augend);
- printf("min_addend:\t%d\n", opts->min_addend);
- printf("max_addend:\t%d\n", opts->max_addend);
-
- printf("subtraction_allowed\t%d\n", opts->subtraction_allowed);
- printf("min_minuend:\t%d\n", opts->min_minuend);
- printf("max_minuend:\t%d\n", opts->max_minuend);
- printf("min_subtrahend:\t%d\n", opts->min_subtrahend);
- printf("max_subtrahend:\t%d\n", opts->max_subtrahend);
-
- printf("multiplication_allowed:\t%d\n", opts->multiplication_allowed);
- printf("min_multiplier:\t%d\n", opts->min_multiplier);
- printf("max_multiplier:\t%d\n", opts->max_multiplier);
- printf("min_multiplicand:\t%d\n", opts->min_multiplicand);
- printf("max_multiplicand:\t%d\n", opts->max_multiplicand);
-
- printf("division_allowed:\t%d\n", opts->division_allowed);
- printf("min_divisor:\t%d\n",opts->min_divisor);
- printf("max_divisor:\t%d\n", opts->max_divisor);
- printf("min_quotient:\t%d\n", opts->min_quotient);
- printf("max_quotient:\t%d\n", opts->max_quotient);
-}
-
/* prints struct to stdout for testing purposes */
void print_game_options(game_option_type* opts)
{
@@ -795,6 +674,15 @@
printf("use_keypad:\t%d\n", opts->use_keypad);
printf("reuse_questions:\t%d\n", opts->reuse_questions);
printf("speed:\t%f\n", opts->speed);
+ printf("allow_speedup:\t%d\n", opts->allow_speedup);
+ printf("speedup_factor:\t%f\n", opts->speedup_factor);
+ printf("max_speed:\t%f\n", opts->max_speed);
+ printf("slow_after_wrong:\t%d\n", opts->slow_after_wrong);
+ printf("extra_comets:\t%d\n", opts->extra_comets);
+ printf("max_comets:\t%d\n", opts->max_comets);
+ printf("num_cities:\t%d\n", opts->num_cities);
+ printf("num_bkgds:\t%d\n", opts->num_bkgds);
+ printf("max_city_colors:\t%d\n", opts->max_city_colors);
}
/* Set the application's icon: */
Modified: tuxmath/trunk/src/tuxmath.c
===================================================================
--- tuxmath/trunk/src/tuxmath.c 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/tuxmath.c 2006-05-16 17:05:20 UTC (rev 10)
@@ -27,7 +27,7 @@
#include "tuxmath.h"
/* global data: */
-math_option_type* math_options; /* used by setup.c, options.c, game.c */
+/*MC_Options* math_options; used by setup.c, options.c, game.c */
game_option_type* game_options; /* used by setup.c, options.c, game.c */
int main(int argc, char * argv[])
Modified: tuxmath/trunk/src/tuxmath.h
===================================================================
--- tuxmath/trunk/src/tuxmath.h 2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/tuxmath.h 2006-05-16 17:05:20 UTC (rev 10)
@@ -20,44 +20,9 @@
#ifndef TUXMATH_H
#define TUXMATH_H
-/* This struct contains all options that determine what */
-/* math questions are asked during a game */
-typedef struct math_option_type {
- /* general math options */
- int allow_neg_answer;
- int max_answer;
- int max_questions;
- int format_answer_last;
- int format_answer_first;
- int format_answer_middle;
- int question_copies;
- /* addition options */
- int addition_allowed;
- int min_augend; /* the "augend" is the first addend i.e. "a" in "a + b = c" */
- int max_augend;
- int min_addend; /* options for the other addend */
- int max_addend;
- /* subtraction options */
- int subtraction_allowed;
- int min_minuend; /* minuend - subtrahend = difference */
- int max_minuend;
- int min_subtrahend;
- int max_subtrahend;
- /* multiplication options */
- int multiplication_allowed;
- int min_multiplier; /* multiplier * multiplicand = product */
- int max_multiplier;
- int min_multiplicand;
- int max_multiplicand;
- /* division options */
- int division_allowed;
- int min_divisor; /* dividend/divosor = quotient */
- int max_divisor;
- int min_quotient;
- int max_quotient;
-} math_option_type;
+#undef TUXMATH_DEBUG /* for conditional compilation of debugging output */
-/* this struct contains all other options regarding general */
+/* this struct contains all options regarding general */
/* gameplay but not having to do with math questions per se */
typedef struct game_option_type {
/* general game options */
@@ -70,63 +35,40 @@
int reuse_questions;
float speed;
int allow_speedup;
- int max_comets;
+ float speedup_factor;
+ float max_speed;
+ int slow_after_wrong;
+ int extra_comets;
+ int max_comets; /*FIXME not being used */
/* not sure the rest of these belong in here */
int num_cities; /* MUST BE AN EVEN NUMBER! */
int num_bkgds;
int max_city_colors;
} game_option_type;
+
/* make option data accessible to rest of program */
-extern math_option_type* math_options; /* used by setup.c, options.c, game.c */
extern game_option_type* game_options; /* used by setup.c, options.c, game.c */
-/* default values for math_options */
-#define DEFAULT_ALLOW_NEG_ANSWER 0
-#define DEFAULT_MAX_ANSWER 144
-#define DEFAULT_MAX_QUESTIONS 5000
-#define DEFAULT_FORMAT_ANSWER_LAST 1 /* question format is: a + b = ? */
-#define DEFAULT_FORMAT_ANSWER_FIRST 0 /* question format is: ? + b = c */
-#define DEFAULT_FORMAT_ANSWER_MIDDLE 0 /* question format is: a + ? = c */
-#define DEFAULT_QUESTION_COPIES 1 /* how many times each question is put in list */
-
-#define DEFAULT_ADDITION_ALLOWED 1
-#define DEFAULT_MIN_AUGEND 0 /* the "augend" is the first addend i.e. "a" in "a + b = c" */
-#define DEFAULT_MAX_AUGEND 12
-#define DEFAULT_MIN_ADDEND 0
-#define DEFAULT_MAX_ADDEND 12
-
-#define DEFAULT_SUBTRACTION_ALLOWED 1 /* minuend - subtrahend = difference */
-#define DEFAULT_MIN_MINUEND 0
-#define DEFAULT_MAX_MINUEND 24
-#define DEFAULT_MIN_SUBTRAHEND 0
-#define DEFAULT_MAX_SUBTRAHEND 12
-
-#define DEFAULT_MULTIPLICATION_ALLOWED 1
-#define DEFAULT_MIN_MULTIPLIER 0 /* multiplier * multiplicand = product */
-#define DEFAULT_MAX_MULTIPLIER 12
-#define DEFAULT_MIN_MULTIPLICAND 0
-#define DEFAULT_MAX_MULTIPLICAND 6
-
-#define DEFAULT_DIVISION_ALLOWED 0 /* dividend/divisor = quotient */
-#define DEFAULT_MIN_DIVISOR 0 /* note - generate_list() will prevent */
-#define DEFAULT_MAX_DIVISOR 3 /* questions with division by zero. */
-#define DEFAULT_MIN_QUOTIENT 0
-#define DEFAULT_MAX_QUOTIENT 3
-
/* default values for game_options */
#define DEFAULT_USE_SOUND 1
#define DEFAULT_FULLSCREEN 0
#define DEFAULT_USE_BKGD 1
#define DEFAULT_DEMO_MODE 0
#define DEFAULT_OPER_OVERRIDE 0
-#define DEFAULT_USE_KEYPAD 1
+#define DEFAULT_USE_KEYPAD 0
#define DEFAULT_REUSE_QUESTIONS 0
#define DEFAULT_SPEED 1
-#define DEFAULT_ALLOW_SPEEDUP 0
-#define DEFAULT_MAX_COMETS 2 /* CHANGED FROM 10 BY DSB */
+#define DEFAULT_ALLOW_SPEEDUP 1
+#define DEFAULT_SPEEDUP_FACTOR 1
+#define DEFAULT_MAX_SPEED 10
+#define DEFAULT_SLOW_AFTER_WRONG 0
+#define DEFAULT_EXTRA_COMETS 2
+#define DEFAULT_MAX_COMETS 4
#define DEFAULT_NUM_CITIES 4 /* MUST BE AN EVEN NUMBER! */
#define DEFAULT_NUM_BKGDS 5
#define DEFAULT_MAX_CITY_COLORS 4
+/* NOTE: default values for math options are now in mathcards.h */
+
#endif
More information about the Tux4kids-commits
mailing list