[Tux4kids-commits] [SCM] tuxhistory - Educational history game branch, master, updated. f0b0d00e71233d5597746899321ab1e525418e74
julio (none)
julio at julio-desktop.
Wed May 26 01:29:59 UTC 2010
The following commit has been merged in the master branch:
commit 0420a483ced5eea1bdef484f96de11699cba0e67
Author: julio <julio at julio-desktop.(none)>
Date: Tue May 25 17:36:26 2010 -0500
Files removed manually
diff --git a/src/campaign.c b/src/campaign.c
deleted file mode 100644
index 7a3bf00..0000000
--- a/src/campaign.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * campaign.c - handle TuxMath's 'Mission mode'
- *
- * Author: B. Luchen
- */
-
-#include "campaign.h"
-#include "tuxhistory.h"
-#include "credits.h"
-#include "titlescreen.h"
-#include "game.h"
-#include "fileops.h"
-#include "mathcards.h"
-#include "options.h"
-
-
-void briefPlayer(int stage); //show text introducing the given stage
-void readStageSettings(int stage);
-void readRoundSettings(int stage, int round);
-void showGameOver();
-void showGameWon();
-
-char* stagenames[NUM_STAGES] = {"cadet", "scout", "ranger", "ace", "commando"};
-
-int start_campaign()
-{
- int i, j;
- int gameresult = 0, endcampaign = 0;
- char roundmessage[10];
- char endtext[2][MAX_LINEWIDTH] = {N_("Congratulations! You win!"), " "};
- printf("Entering start_campaign()\n");
-
-
- for (i = 0; i < NUM_STAGES; ++i)
- {
- printf("Stage %s\n", stagenames[i]);
- briefPlayer(i);
- for (j = 1; j <= NUM_ROUNDS; ++j)
- {
- printf("Round %d\n", j);
-
- //read in settings
- read_named_config_file("campaign/campaign");
- readStageSettings(i);
- readRoundSettings(i, j);
- Opts_SetKeepScore(0);
-
- snprintf(roundmessage, 10, "%s %d", N_("Round"), j);
- game_set_start_message(roundmessage, "", "", "");
-
- MC_PrintMathOptions(stdout, 0);
-
- //play!
- printf("Starting game...\n");
- gameresult = game();
-
- //move on if we've won, game over if not
- if (gameresult == GAME_OVER_WON)
- ;
- else if (gameresult == GAME_OVER_LOST)
- {
- showGameOver();
- endcampaign = 1;
- }
- else if (gameresult == GAME_OVER_ERROR)
- {
- DEBUGMSG(debug_game, "Error!\n");
- endcampaign = 1;
- }
-#ifndef TESTING_CAMPAIGN
- else if (gameresult == GAME_OVER_ESCAPE)
- {
- DEBUGMSG(debug_game, "hit escape\n");
- endcampaign = 1;
- }
-#endif
- else
- {
- printf("gameresult = %d\n", gameresult);
- endcampaign = 0;
- }
-
- if (endcampaign)
- return 0;
- }
-
- //if we've beaten the last stage, there is no bonus, skip to win sequence
- if (i == NUM_STAGES - 1)
- {
- showGameWon();
- break;
- }
-/* //bonus round
- readStageSettings(i);
- readRoundSettings(i, -1);
- game_set_start_message("Bonus", "", "", "");
- game();
-*/
- }
- scroll_text(endtext, screen->clip_rect, 3);
- return 0;
-}
-
-void briefPlayer(int stage)
-{
-/* NOTE: the convention has changed. Use " " for a blank line (note
- the space), and use "" (rather than NULL) for the termination
- string. This is a consequence of the linewrapping code. TEH Feb
- 2009. */
-
- const char briefings[NUM_STAGES][MAX_LINES][MAX_LINEWIDTH] =
- {
- //cadet
- {
- {N_("-[Esc] to skip")},
- {N_("Mission One: Careful Cadet")},
- {"--------------------------"},
- {N_("I'm so glad you've come!")},
- {" "},
- {N_("The penguins need your help! Comets are falling from the sky, and are melting the penguins' igloos. To save their homes, we need you to find the secret code that will zap each comet.")},
- {" "},
- {N_("Do your best!")},
- {""}
- },
- //scout
- {
- {N_("-[Esc] to skip")},
- {N_("Mission Two: Smart Scout")},
- {"------------------------"},
- {N_("Great job! Since you saved the penguins' homes, we are promoting you to Scout. Scouts are good for keeping an eye out for trouble...")},
- {" "},
- {N_("...like what's happening right now! The TakeAways have come, and they're sending new, trickier comets against the penguins!")},
- {N_("But you can save them!")},
- {""}
- },
- //ranger
- {
- {"-[Esc] to skip"},
- {N_("Mission Three: Royal Ranger")},
- {"---------------------------"},
- {N_("You've done it again! The Penguin Emperor has chosen you to join his team of Rangers that help protect the city. We're sending you there now...")},
- {" "},
- {N_("...oh no! Now the Emperor himself is under attack, from new types of comets: these problems are multiplying! To fight these, you need great skill. We think you can do it. Join the Rangers and help save the city!")},
- {""}
- },
- //ace
- {
- {N_("-[Esc] to skip")},
- {N_("Mission Four: Imperial Ace")},
- {"--------------------------"},
- {N_("You did it! The Emperor wants to thank you in person. We are taking you to his ice palace for a great honor: you will become the Imperial Ace!")},
- {" "},
- {N_("But right in the middle of the ceremony, a new attack from the land of Division starts!")},
- {N_("Now is no time for resting; the city needs your help!")},
- {""}
- },
- //commando
- {
- {N_("-[Esc] to skip")},
- {N_("Final Mission: Computing Commando")},
- {"---------------------------------"},
- {N_("Penguin scientists have learned that all these attacks are coming from a secret base, and they need you to go fight the final battle. They also give you this clue: first do multiplication and division, and then do addition and subtraction.")},
- {N_("I hope that hint helps!")},
- {" "},
- {N_("This is it! You can stop these attacks forever, Commando!")},
- {""}
- },
- };
-
-
- char* sprites[] = {
- "sprites/tux_helmet_yellowd.png",
- "sprites/tux_helmet_greend.png",
- "sprites/tux_helmet_blued.png",
- "sprites/tux_helmet_redd.png",
- "sprites/tux_helmet_blackd.png"
- };
-
- SDL_Surface* icon = NULL;
- SDL_Rect textarea = screen->clip_rect;
- SDL_Surface* loadedsprite = LoadImage(sprites[stage], IMG_REGULAR|IMG_NOT_REQUIRED);
-
-
-
- if (loadedsprite) //stretch the tiny sprite to 3x
- {
- icon = zoom(loadedsprite, loadedsprite->w*3, loadedsprite->h*3);
- textarea.x = icon->w;
- textarea.y = icon->h;
- textarea.w = screen->w - icon->w;
- textarea.h = screen->h - icon->h;
- }
-
- SDL_FillRect(screen, NULL, 0);
- //TransWipe(black, RANDOM_WIPE, 10, 20);
- //show this stage's text
- DEBUGMSG(debug_game, "Briefing\n");
-
- SDL_BlitSurface(icon, NULL, screen, NULL);
-
- linewrap_list(briefings[stage], wrapped_lines, 40, MAX_LINES, MAX_LINEWIDTH);
- scroll_text(wrapped_lines, textarea, 1);
-
- DEBUGMSG(debug_game, "Finished briefing\n");
-
- SDL_FreeSurface(loadedsprite);
- SDL_FreeSurface(icon);
-}
-
-void readStageSettings(int stage)
-{
- char fn[PATH_MAX];
- snprintf(fn,PATH_MAX, "campaign/%s/%s", stagenames[stage], stagenames[stage]);
- read_named_config_file(fn);
-}
-
-void readRoundSettings(int stage, int round)
-{
- char fn[PATH_MAX];
- if (round == -1)
- snprintf(fn, PATH_MAX, "campaign/%s/bonus", stagenames[stage]);
- else
- snprintf(fn,PATH_MAX, "campaign/%s/round%d", stagenames[stage], round);
- read_named_config_file(fn);
-}
-
-void showGameOver()
-{
- const char text[2][MAX_LINEWIDTH] = {N_("Sorry, try again!"), ""};
- linewrap_list(text, wrapped_lines, 40, MAX_LINES, MAX_LINEWIDTH);
- scroll_text(wrapped_lines, screen->clip_rect, 3);
-}
-
-void showGameWon()
-{
- const char text[2][MAX_LINEWIDTH] = {N_("Mission accomplished. The galaxy is safe!"), ""};
- linewrap_list(text, wrapped_lines, 40, MAX_LINES, MAX_LINEWIDTH);
- scroll_text(wrapped_lines, screen->clip_rect, 3);
-}
diff --git a/src/campaign.h b/src/campaign.h
deleted file mode 100644
index a60aebb..0000000
--- a/src/campaign.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef CAMPAIGN_H
-#define CAMPAIGN_H
-
-/*
- * campaign.h - prose and function declarations for TuxMath's 'Mission mode'
- *
- * Author: B. Luchen
- */
-
-#include "SDL_extras.h"
-#include "tuxhistory.h"
-#include "linewrap.h"
-
-
-//#define TESTING_CAMPAIGN //allow ESC to skip missions instead of exiting
-
-#define NUM_STAGES 5
-#define NUM_ROUNDS 3
-
-/* NOTE - moved 'briefings' into campaign.c' as data local to briefPlayer() */
-/* because that was the only place it was used - just occupying memory. */
-
-int start_campaign();
-
-#endif // CAMPAIGN_H
diff --git a/src/exercise_mathcards.c b/src/exercise_mathcards.c
deleted file mode 100644
index ee70135..0000000
--- a/src/exercise_mathcards.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-* C Implementation: exercise_mathcards
-*
-* Description:
-*
-*
-* Author: David Bruce <davidstuartbruce at gmail.com>, (C) 2007
-*
-* Copyright: See COPYING file that comes with this distribution
-*
-*/
-
-#include <stdio.h>
-#include "mathcards.h"
-
-int main()
-{
- int i, iter, op;
- MC_FlashCard c;
-
- MC_Initialize();
-
- for (i = 0; i < 100; i++)
- {
-
- fprintf(stderr, "\n\nGame: i = %d\n", i);
- op = rand() % 2;
- MC_SetAddAllowed(op);
- op = rand() % 2;
- MC_SetSubAllowed(op);
- op = rand() % 2;
- MC_SetMultAllowed(op);
- op = rand() % 2;
- MC_SetDivAllowed(op);
-
- if (!MC_StartGame())
- continue;
-
- iter = 0;
-
- while(!MC_MissionAccomplished())
- {
- MC_NextQuestion(&c);
- op = rand() % 2;
- if (op)
- MC_AnsweredCorrectly(&c);
- else
- MC_NotAnsweredCorrectly(&c);
- iter++;
- }
-// MC_EndGame();
- }
- return 1;
-}
diff --git a/src/factoroids.c b/src/factoroids.c
deleted file mode 100644
index d3652ca..0000000
--- a/src/factoroids.c
+++ /dev/null
@@ -1,2222 +0,0 @@
-/************************************************************
- * factoroids.c *
- * *
- * Description: Code for the factor and fraction activity *
- * *
- * Author: Jesus M. Mager H. (fongog at gmail.com) 2008 *
- * Copyright: GPL v3 or later *
- * *
- * Also significantly enhanced by Tim Holy - 2008 *
- * *
- * Code based on the work made by: *
- * Bill Kendrick (vectoroids 1.1.0) *
- * and Bill Kendrick, David Bruce, Tim Holy *
- * and others (Tuxmath 1.6.3) *
- * *
- * TuxMath *
- * Part of "Tux4Kids" Project *
- * http://tux4kids.alioth.debian.org/ *
- ************************************************************/
-
-#include "tuxhistory.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "SDL.h"
-#ifndef NOSOUND
-#include "SDL_mixer.h"
-#endif
-#include "SDL_image.h"
-#include "SDL_rotozoom.h"
-#include "SDL_extras.h"
-
-#include "game.h"
-#include "fileops.h"
-#include "setup.h"
-#include "mathcards.h"
-#include "loaders.h"
-#include "titlescreen.h"
-#include "options.h"
-
-#define FPS 15 /* 15 frames per second */
-#define MS_PER_FRAME (1000 / FPS)
-#define BASE_RES_X 1280
-
-#define MAX_LASER 5
-#define MAX_ASTEROIDS 50
-#define NUM_TUXSHIPS 2
-#define NUM_SPRITES 11
-#define TUXSHIP_LIVES 3
-#define DEG_PER_ROTATION 2
-#define NUM_OF_ROTO_IMGS 360/DEG_PER_ROTATION
-/* TUXSHIP_DECEL controls "friction" - 1 means ship glides infinitely, 0 stops it instantly */
-#define TUXSHIP_DECEL 0.8
-#define DEG_TO_RAD 0.0174532925
-#define MAX(a,b) (((a) > (b)) ? (a) : (b))
-
-/********* Enumerations ***********/
-
-enum{
- FACTOROIDS_GAME,
- FRACTIONS_GAME
-};
-
-/********* Structures *********/
-
-typedef struct colorRGBA_type {
- Uint8 r;
- Uint8 g;
- Uint8 b;
- Uint8 a;
-} ColorRGBA_type;
-
-typedef struct asteroid_type {
- int alive, size;
- int angle, angle_speed;
- int xspeed, yspeed;
- int x, y;
- int rx, ry;
- int centerx, centery;
- int radius;
- int fact_number;
- int isprime;
- int a, b; /* a / b */
- int count;
-} asteroid_type;
-
-
-typedef struct tuxship_type {
- int lives, size;
- int xspeed, yspeed;
- int x, y;
- int rx, ry;
- int x1,y1,x2,y2,x3,y3;
- int radius;
- int centerx, centery;
- int angle;
- int hurt, hurt_count;
- int count;
-} tuxship_type;
-
-
-typedef struct FF_laser_type{
- int alive;
- int x, y;
- int destx,desty;
- int r, g, b;
- int count;
- int angle;
- int m;
-} FF_laser_type;
-
-
-typedef struct {
- int x_is_blinking;
- int extra_life_is_blinking;
- int laser_enabled;
-} help_controls_type;
-
-/********* Global vars ************/
-
-/* Trig junk: (thanks to Atari BASIC for this) */
-
-static int trig[12] = {
- 1024,
- 1014,
- 984,
- 935,
- 868,
- 784,
- 685,
- 572,
- 448,
- 316,
- 117,
- 0
-};
-
-// ControlKeys
-static int left_pressed;
-static int right_pressed;
-static int up_pressed;
-static int shift_pressed;
-static int shoot_pressed;
-
-// GameControl
-static int game_status;
-//static int gameover_counter;
-static int escape_received;
-
-//SDL_Surfaces:
-static SDL_Surface* IMG_tuxship[NUM_OF_ROTO_IMGS];
-static SDL_Surface* IMG_asteroids1[NUM_OF_ROTO_IMGS];
-static SDL_Surface* IMG_asteroids2[NUM_OF_ROTO_IMGS];
-static SDL_Surface* bkgd = NULL; //640x480 background (windowed)
-static SDL_Surface* scaled_bkgd = NULL; //native resolution (fullscreen)
-
-
-// Game type
-static int FF_game;
-
-// Game vars
-static int score;
-static int wave;
-static int paused;
-static int escape_received;
-static int game_status;
-static int SDL_quit_received;
-static int quit;
-static int digits[3];
-static int num;
-
-static int neg_answer_picked;
-static int tux_pressing;
-static int doing_answer;
-static int level_start_wait;
-static int tux_img;
-//static int FF_level;
-
-static asteroid_type* asteroid = NULL;
-static tuxship_type tuxship;
-static FF_laser_type laser[MAX_LASER];
-
-static int NUM_ASTEROIDS;
-static int counter;
-static int xdead, ydead, isdead, countdead;
-static int roto_speed;
-
-/*************** The Factor and Fraction Activity Game Functions ***************/
-
-/* Local function prototypes: */
-
-static int FF_init(void);
-static void FF_intro(void);
-
-static void FF_handle_ship(void);
-static void FF_handle_asteroids(void);
-static void FF_handle_answer(void);
-static int check_exit_conditions(void);
-static void FF_draw(void);
-static void FF_draw_bkgr(void);
-static void FF_draw_led_console(void);
-static void draw_console_image(int i);
-
-static SDL_Surface* current_bkgd()
- { return screen->flags & SDL_FULLSCREEN ? scaled_bkgd : bkgd; }
-
-static void FF_add_level(void);
-static int FF_over(int game_status);
-static void FF_exit_free(void);
-
-static int FF_add_laser(void);
-static int FF_add_asteroid(int x, int y, int xspeed, int yspeed, int size, int angle, int angle_speed, int fact_num, int a, int b, int new_wave);
-static int FF_destroy_asteroid(int i, float xspeed, float yspeed);
-
-static void FF_ShowMessage(char* str1, char* str2, char* str3, char* str4);
-
-static SDL_Surface* get_asteroid_image(int size,int angle);
-static int AsteroidColl(int astW,int astH,int astX,int astY,
- int x, int y);
-static int is_prime(int num);
-static int fast_cos(int angle);
-static int fast_sin(int angle);
-static void game_handle_user_events(void);
-static int game_mouse_event(SDL_Event event);
-
-/************** factors(): The factor main function ********************/
-void factors(void)
-{
- Uint32 last_time, now_time;
-
- quit = 0;
- counter = 0;
- tux_img = IMG_TUX_CONSOLE1;
-
- DEBUGMSG(debug_factoroids, "Entering factors():\n");
-
- FF_game = FACTOROIDS_GAME;
-
- if (!FF_init())
- {
- fprintf(stderr, "FF_init() failed!\n");
- FF_exit_free();
- return;
- }
-
- while (game_status == GAME_IN_PROGRESS)
- {
- last_time = SDL_GetTicks();
- counter++;
-
- if(counter%15 == 0)
- {
- if(tux_img<IMG_TUX_CONSOLE4)
- tux_img++;
- else
- tux_img=IMG_TUX_CONSOLE1;
- }
-
- game_handle_user_events();
-
- FF_handle_ship();
- FF_handle_asteroids();
- FF_handle_answer();
- FF_draw();
- SDL_Flip(screen);
-
- game_status = check_exit_conditions();
-
- if (paused)
- {
- pause_game();
- paused = 0;
- }
-
-
-#ifndef NOSOUND
- if (Opts_UsingSound())
- {
- //...when the music's over, turn out the lights!
- //...oops, wrong song! Actually, we just pick next music at random:
- 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 + MS_PER_FRAME)
- {
- //Prevent any possibility of a time wrap-around
- // (this is a very unlikely problem unless there is an SDL bug
- // or you leave tuxmath running for 49 days...)
- now_time = (last_time+MS_PER_FRAME) - now_time; // this holds the delay
- if (now_time > MS_PER_FRAME)
- now_time = MS_PER_FRAME;
- SDL_Delay(now_time);
- }
- }
- FF_over(game_status);
-}
-
-
-/************** fractions(): The fractions main function ********************/
-void fractions(void)
-{
-
- Uint32 last_time, now_time;
-
- quit = 0;
- counter = 0;
- tux_img = IMG_TUX_CONSOLE1;
-
- DEBUGMSG(debug_factoroids, "Entering factors():\n");
- /*****Initalizing the Factor activiy *****/
- FF_game = FRACTIONS_GAME;
-
- if (!FF_init())
- {
- fprintf(stderr, "FF_init() failed!\n");
- FF_exit_free();
- return;
- }
-
- /************ Main Loop **************/
- while (game_status == GAME_IN_PROGRESS)
- {
- last_time = SDL_GetTicks();
- counter++;
-
- if(counter%15 == 0)
- {
- if(tux_img < IMG_TUX_CONSOLE4)
- tux_img++;
- else
- tux_img = IMG_TUX_CONSOLE1;
- }
-
- game_handle_user_events();
-
- FF_handle_ship();
- FF_handle_asteroids();
- FF_handle_answer();
- FF_draw();
- SDL_Flip(screen);
-
- game_status = check_exit_conditions();
-
- if (paused)
- {
- pause_game();
- paused = 0;
- }
-
-
-#ifndef NOSOUND
- if (Opts_UsingSound())
- {
- 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 + MS_PER_FRAME)
- {
- //Prevent any possibility of a time wrap-around
- // (this is a very unlikely problem unless there is an SDL bug
- // or you leave tuxmath running for 49 days...)
- now_time = (last_time + MS_PER_FRAME) - now_time; // this holds the delay
- if (now_time > MS_PER_FRAME)
- now_time = MS_PER_FRAME;
- SDL_Delay(now_time);
- }
- }
- FF_over(game_status);
-}
-
-
-/************ Initialize all vars... ****************/
-static int FF_init(void)
-{
- int i;
- float zoom;
-
- SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
- SDL_Flip(screen);
-
- FF_intro();
-
- if(screen->h < 600 && screen->w < 800)
- zoom = 0.65;
- else
- zoom = (float)screen->w/(float)BASE_RES_X;
-
- DEBUGCODE(debug_factoroids)
- printf("The zoom factor is: %f\n", zoom);
-
- /*************** Precalculating software rotation ***************/
-
- for(i = 0; i < NUM_OF_ROTO_IMGS; i++)
- {
- //rotozoomSurface (SDL_Surface *src, double angle, double zoom, int smooth);
- IMG_tuxship[i] = rotozoomSurface(images[IMG_SHIP01], i * DEG_PER_ROTATION, zoom, 1);
-
- if (IMG_tuxship[i] == NULL)
- {
- fprintf(stderr,
- "\nError: rotozoomSurface() of images[IMG_SHIP01] for i = %d returned NULL\n", i);
- return 0;
- }
-
- IMG_asteroids1[i] = rotozoomSurface(images[IMG_ASTEROID1], i * DEG_PER_ROTATION, zoom, 1);
-
- if (IMG_asteroids1[i] == NULL)
- {
- fprintf(stderr,
- "\nError: rotozoomSurface() of images[IMG_ASTEROID1] for i = %d returned NULL\n", i);
- return 0;
- }
-
- IMG_asteroids2[i] = rotozoomSurface(images[IMG_ASTEROID2], i*DEG_PER_ROTATION, zoom, 1);
-
- if (IMG_asteroids2[i] == NULL)
- {
- fprintf(stderr,
- "\nError: rotozoomSurface() of images[IMG_ASTEROID2] for i = %d returned NULL\n", i);
- return 0;
- }
- }
-
-
- /******** Set up properly scaled and optimized background surfaces: *********/
- /* NOTE - optimization code moved into LoadBothBkgds() so rest of program */
- /* can take advantage of it - DSB */
-
- LoadBothBkgds("factoroids/gbstars.png", &scaled_bkgd, &bkgd);
-
- if (bkgd == NULL || scaled_bkgd == NULL)
- {
- fprintf(stderr,
- "\nError: could not scale background\n");
- return 0;
- }
-
-
- // Allocate memory
- asteroid = NULL; // set in case allocation fails partway through
- asteroid = (asteroid_type *) malloc(MAX_ASTEROIDS * sizeof(asteroid_type));
-
- if (asteroid == NULL)
- {
- printf("Allocation of asteroids failed");
- return 0;
- }
-
- NUM_ASTEROIDS = 4;
-
- /**************Setting up the ship values! **************/
- tuxship.x = ((screen->w)/2) - 20;
- tuxship.y = ((screen->h)/2) - 20;
- tuxship.lives = TUXSHIP_LIVES;
- tuxship.hurt = 0;
- tuxship.hurt_count = 0;
- tuxship.angle = 90;
- tuxship.xspeed = 0;
- tuxship.yspeed = 0;
- tuxship.radius = (images[IMG_SHIP01]->h)/2;
-
- tuxship.x1 = images[IMG_SHIP01]->w-(images[IMG_SHIP01]->w/8);
- tuxship.y1 = images[IMG_SHIP01]->h/2;
- tuxship.x2 = images[IMG_SHIP01]->w/8;
- tuxship.y2 = images[IMG_SHIP01]->h/8;
- tuxship.x3 = images[IMG_SHIP01]->w/8;
- tuxship.y3 = images[IMG_SHIP01]->h-(images[IMG_SHIP01]->h/8);
-
- /* --- reset all controls: --- */
- left_pressed = 0;
- right_pressed = 0;
- up_pressed = 0;
- shift_pressed = 0;
- shoot_pressed = 0;
-
- score = 0;
- wave = 0;
- xdead = 0;
- ydead = 0;
- isdead = 0;
- countdead = 0;
- escape_received = 0;
- game_status = GAME_IN_PROGRESS;
-
- FF_add_level();
-
- for (i = 0; i < MAX_LASER; i++)
- laser[i].alive = 0;
-
- // Wait for click or keypress to start (get out if user presses Esc) :
- while(1)
- {
- SDL_PollEvent(&event);
- if (event.type == SDL_QUIT)
- {
- SDL_quit_received = 1;
- quit = 1;
- return 1;
- }
- else if (event.type == SDL_MOUSEBUTTONDOWN)
- {
- return 1;
- }
- else if (event.type == SDL_KEYDOWN)
- {
- if (event.key.keysym.sym == SDLK_ESCAPE)
- escape_received = 1;
- return 1;
- }
- }
-}
-
-
-static void FF_intro(void)
-{
- static SDL_Surface* IMG_factors;
- static SDL_Surface* IMG_fractions;
-
-// SDL_Event event;
- SDL_Rect rect;
-
- float zoom;
-
- if(screen->h < 600 && screen->w < 800)
- zoom = 0.65;
- else
- zoom=(float)screen->w/(float)BASE_RES_X;
-
- IMG_factors = rotozoomSurface(images[IMG_FACTOROIDS], 0, zoom, 1);
- IMG_fractions = rotozoomSurface(images[IMG_FACTORS], 0, zoom, 1);
-
- FF_draw_bkgr();
- if(FF_game == FACTOROIDS_GAME)
- {
-
- rect.x = (screen->w/2) - (IMG_factors->w/2);
- rect.y = (screen->h)/7;
- SDL_BlitSurface(IMG_factors, NULL, screen, &rect);
- FF_ShowMessage(_("FACTOROIDS: to win, you need destroy all the asteroids."),
- _("Use the arrow keys to turn or go forward. Aim at an asteroid,"),
- _("type one of its factors, and press space or return"),
- _("to split it into its factors. Rocks with prime numbers are destroyed!"));
- SDL_BlitSurface(IMG_asteroids1[3],NULL,screen,&rect);
- }
- else if (FF_game == FRACTIONS_GAME)
- {
- rect.x = (screen->w/2)-(IMG_fractions->w/2);
- rect.y = (screen->h)/7;
- SDL_BlitSurface(IMG_fractions,NULL,screen,&rect);
- FF_ShowMessage(_("FRACTIONS: to win, you need destroy all the asteroids"),
- _("Use the arrow keys to turn or go forward. Aim at an asteroid,"),
- _("type a number that can simplify the fraction, and press space or return"),
- _("to split it. Destroy fractions that can not be further simplified in a single shot!"));
- }
-
- SDL_FreeSurface(IMG_factors);
- SDL_FreeSurface(IMG_fractions);
-}
-
-static void FF_handle_ship(void)
-{
-//FIXME - am I missing something -- doesn't this just reduce to
-//"tuxship.centerx = tuxship.x" and likewise for y???
-/****************** Ship center... ******************/
-
- tuxship.centerx = ((IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->w)/2) +
- (tuxship.x - (IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->w/2));
- tuxship.centery = ((IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->h)/2) +
- (tuxship.y - (IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->h/2));
-
-/******************* Ship live *********************/
-
- if(tuxship.hurt)
- {
- tuxship.hurt_count--;
- if(tuxship.hurt_count <= 0)
- tuxship.hurt = 0;
- }
-/****************** Rotate Ship *********************/
-
- if(right_pressed || left_pressed)
- {
- if(roto_speed < 10)
- {
- roto_speed = roto_speed + 2;
- }
- }
- else
- {
- roto_speed = 1;
- }
-
- if (right_pressed)
- {
- tuxship.angle = tuxship.angle - DEG_PER_ROTATION * roto_speed;
- if (tuxship.angle < 0)
- tuxship.angle = tuxship.angle + 360;
-
- tuxship.x1= fast_cos(DEG_PER_ROTATION*-roto_speed) * tuxship.centerx
- -fast_sin(DEG_PER_ROTATION*-roto_speed) * tuxship.centery;
- tuxship.y1= fast_sin(DEG_PER_ROTATION*-roto_speed) * tuxship.centerx
- +fast_cos(DEG_PER_ROTATION*-roto_speed) * tuxship.centery;
-
- }
- else if (left_pressed)
- {
- tuxship.angle=tuxship.angle + DEG_PER_ROTATION * roto_speed;
- if (tuxship.angle >= 360)
- tuxship.angle = tuxship.angle - 360;
-
- tuxship.x1= fast_cos(DEG_PER_ROTATION*roto_speed) * tuxship.centerx
- -fast_sin(DEG_PER_ROTATION*roto_speed) * tuxship.centery;
- tuxship.y1= fast_sin(DEG_PER_ROTATION*roto_speed * tuxship.centerx
- +fast_cos(DEG_PER_ROTATION*roto_speed)) * tuxship.centery;
-
- }
-
-/**************** Move, and increse speed ***************/
-
-
- if (up_pressed && (tuxship.lives > 0))
- {
- tuxship.xspeed = tuxship.xspeed + ((fast_cos(tuxship.angle >> 3) * 3) >> 10);
- tuxship.yspeed = tuxship.yspeed - ((fast_sin(tuxship.angle >> 3) * 3) >> 10);
- }
- else
- {
- if ((counter % 2) == 0)
- {
- tuxship.xspeed = tuxship.xspeed * TUXSHIP_DECEL;
- tuxship.yspeed = tuxship.yspeed * TUXSHIP_DECEL;
- }
- }
-
- tuxship.x = tuxship.x + tuxship.xspeed;
- tuxship.y = tuxship.y + tuxship.yspeed;
-
-/*************** Wrap ship around edges of screen ****************/
-
- if(tuxship.x >= (screen->w))
- tuxship.x = tuxship.x - (screen->w);
- else if (tuxship.x < -60)
- tuxship.x = tuxship.x + (screen->w);
-
- if(tuxship.y >= (screen->h))
- tuxship.y = tuxship.y - (screen->h);
- else if (tuxship.y < -60)
- tuxship.y = tuxship.y + (screen->h);
-
-/**************** Shoot ***************/
- if(shoot_pressed)
- {
- FF_add_laser();
- shoot_pressed=0;
- }
-}
-
-
-static void FF_handle_asteroids(void){
-
- SDL_Surface* surf;
- int i, found=0;
- for (i = 0; i < MAX_ASTEROIDS; i++){
- if (asteroid[i].alive)
- {
-
- found=1;
-
- /*************** Rotate asteroid ****************/
-
- asteroid[i].angle = (asteroid[i].angle + asteroid[i].angle_speed);
-
- // Wrap rotation angle...
-
- if (asteroid[i].angle < 0)
- asteroid[i].angle = asteroid[i].angle + 360;
- else if (asteroid[i].angle >= 360)
- asteroid[i].angle = asteroid[i].angle - 360;
-
- /**************Move the astroids ****************/
- surf=get_asteroid_image(asteroid[i].size,asteroid[i].angle);
-
- asteroid[i].rx = asteroid[i].rx + asteroid[i].xspeed;
- asteroid[i].ry = asteroid[i].ry + asteroid[i].yspeed;
-
- asteroid[i].x = (asteroid[i].rx - (surf->w/2));
- asteroid[i].y = (asteroid[i].ry - (surf->h/2));
-
- // Wrap asteroid around edges of screen:
-
- if (asteroid[i].x >= (screen->w))
- asteroid[i].rx = asteroid[i].rx - (screen->w);
- else if (asteroid[i].x < 0)
- asteroid[i].rx = asteroid[i].rx + (screen->w);
-
- if (asteroid[i].y >= (screen->h))
- asteroid[i].ry = asteroid[i].ry - (screen->h);
- else if (asteroid[i].ry < 0)
- asteroid[i].ry = asteroid[i].ry + (screen->h);
- /**************Center Asteroids**************/
-
- asteroid[i].centerx=((surf->w)/2)+(asteroid[i].x-5);
- asteroid[i].centery=((surf->h)/2)+(asteroid[i].y-5);
-
- /*************** Collisions! ****************/
-
- if(AsteroidColl(surf->w, surf->h, asteroid[i].x, asteroid[i].y, tuxship.centerx, tuxship.centery))
- {
- if(!tuxship.hurt)
- {
- xdead=asteroid[i].centerx;
- ydead=asteroid[i].centery;
-
- tuxship.lives--;
- tuxship.hurt=1;
- tuxship.hurt_count=50;
- FF_destroy_asteroid(i, tuxship.xspeed, tuxship.yspeed);
- playsound(SND_EXPLOSION);
-
- }
- }
- }
- }
- if(!found)
- FF_add_level();
-}
-
-static void FF_handle_answer(void)
-{
-
- num = (digits[0] * 100 +
- digits[1] * 10 +
- digits[2]);
- /* negative answer support DSB */
- if (neg_answer_picked)
- {
- num = -num;
- }
-
- if (!doing_answer)
- {
- return;
- }
-
- doing_answer = 0;
-
- /* Clear digits: */
- digits[0] = 0;
- digits[1] = 0;
- digits[2] = 0;
- neg_answer_picked = 0;
-
-}
-
-static SDL_Surface* get_asteroid_image(int size,int angle)
-{
- if (size == 0)
- return IMG_asteroids1[angle/DEG_PER_ROTATION];
- else
- return IMG_asteroids2[angle/DEG_PER_ROTATION];
-}
-
-static void FF_draw(void){
-
- int i, offset;
- int xnum, ynum;
- char str[64];
- SDL_Surface* surf;
- SDL_Rect dest;
-
- SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
-
- /************ Draw Background ***************/
-
- FF_draw_bkgr();
-
-/******************* Draw laser *************************/
- for (i=0;i<MAX_LASER;i++){
- if(laser[i].alive)
- {
- if(laser[i].count>0)
- {
- laser[i].count--;
- laser[i].x=laser[i].x+tuxship.xspeed;
- laser[i].y=laser[i].y+tuxship.yspeed;
- laser[i].destx=laser[i].destx+tuxship.xspeed;
- laser[i].desty=laser[i].desty+tuxship.yspeed;
- draw_line(laser[i].x, laser[i].y, laser[i].destx, laser[i].desty,
- laser[i].count*18, 0, 0);
- } else if (laser[i].count <= 0)
- {
- laser[i].alive=0;
- }
- }
- }
- /*************** Draw Ship ******************/
-
- if(!tuxship.hurt || (tuxship.hurt && tuxship.hurt_count%2==0)){
- dest.x = (tuxship.x - (IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->w/2));
- dest.y = (tuxship.y - (IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->h/2));
- dest.w = IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->w;
- dest.h = IMG_tuxship[tuxship.angle/DEG_PER_ROTATION]->h;
-
- SDL_BlitSurface(IMG_tuxship[tuxship.angle/DEG_PER_ROTATION], NULL, screen, &dest);
- }
- /************* Draw Asteroids ***************/
- for(i=0; i<MAX_ASTEROIDS; i++){
- if(asteroid[i].alive>0){
-
- xnum=0;
- ynum=0;
-
- dest.x = asteroid[i].x;
- dest.y = asteroid[i].y;
-
- surf=get_asteroid_image(asteroid[i].size,asteroid[i].angle);
-
- dest.w = surf->w;
- dest.h = surf->h;
-
- SDL_BlitSurface(surf, NULL, screen, &dest);
-
- // Wrap the numbers of the asteroids
- if((asteroid[i].centery)>23 && (asteroid[i].centery)<screen->h)
- {
- if((asteroid[i].centerx)>0 && (asteroid[i].centerx)<screen->w)
- {
- xnum=asteroid[i].centerx-3;
- ynum=asteroid[i].centery;
- }
- else if((asteroid[i].centerx)<=0){
- xnum=20;
- ynum=asteroid[i].centery;
- }
- else if((asteroid[i].centerx)<=screen->w){
- xnum=screen->w-20;
- ynum=asteroid[i].centery;
- }
- }
- else if((asteroid[i].centery)<=23)
- {
- xnum=asteroid[i].centerx;
- ynum=23;
- }
- else if((asteroid[i].centery)>=screen->h)
- {
- xnum=asteroid[i].centerx;
- ynum=screen->h-7;
- }
-
- //Draw Numbers
- if(FF_game==FACTOROIDS_GAME)
- {
- sprintf(str, "%.1d", asteroid[i].fact_number);
- draw_nums(str, xnum, ynum);
- }
- else if (FF_game==FRACTIONS_GAME)
- {
- sprintf(str, "%d", asteroid[i].a);
- draw_nums(str, xnum, ynum);
- draw_line(xnum, ynum + 4, xnum + 30, ynum + 4,
- 255, 255, 255);
- sprintf(str, "%d", asteroid[i].b);
- draw_nums(str, xnum, ynum + 35);
- }
- }
- }
- /*************** Draw Steam ***************/
-
- if(isdead)
- {
- dest.x = xdead;
- dest.y = ydead;
- SDL_BlitSurface(images[IMG_STEAM1+countdead], NULL, screen, &dest);
- countdead++;
- if(countdead > 5)
- {
- isdead = 0;
- countdead = 0;
- }
- }
-
- /* Draw wave: */
- if (1)//Opts_BonusCometInterval())
- offset = images[IMG_EXTRA_LIFE]->w + 5;
- else
- offset = 0;
-
- dest.x = offset;
-
- 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, offset+images[IMG_WAVE]->w + (images[IMG_NUMBERS]->w / 10), 0);
-
- /* Draw "score" label: */
- dest.x = (screen->w - ((images[IMG_NUMBERS]->w/10) * 7) -
- images[IMG_SCORE]->w -
- images[IMG_STOP]->w - 5);
- dest.y = 0;
- dest.w = images[IMG_SCORE]->w;
- dest.h = images[IMG_SCORE]->h;
-
- SDL_BlitSurface(images[IMG_SCORE], NULL, screen, &dest);
-
- sprintf(str, "%.6d", score);
- draw_numbers(str,
- screen->w - ((images[IMG_NUMBERS]->w / 10) * 6) - images[IMG_STOP]->w - 5,
- 0);
-
- /* Draw stop button: */
-// if (!help_controls.x_is_blinking || (frame % 10 < 5)) {
- dest.x = (screen->w - images[IMG_STOP]->w);
- dest.y = 0;
- dest.w = images[IMG_STOP]->w;
- dest.h = images[IMG_STOP]->h;
-
- SDL_BlitSurface(images[IMG_STOP], NULL, screen, &dest);
- // }
-
- /************* Draw pre answer ************/
-
-
- if(screen->w < 800 && screen->h < 600)
- {
- sprintf(str, "%.3d", num);
- draw_numbers(str, ((screen->w)/2) - 50, (screen->h) - 30);
- }
- else
- {
- FF_draw_led_console();
- draw_console_image(tux_img);
- }
-
- /************** Draw lives ***************/
- dest.y = screen->h;
- dest.x = 0;
-
- for(i = 1; i <= tuxship.lives; i++)
- {
- if(tuxship.lives <= 5)
- {
- dest.y = dest.y - (images[IMG_TUX_LITTLE]->h);
- SDL_BlitSurface(images[IMG_TUX_LITTLE], NULL, screen, &dest);
- }
- else if(tuxship.lives > 4)
- {
- dest.y = screen->h - (images[IMG_TUX_LITTLE]->h);
- SDL_BlitSurface(images[IMG_TUX_LITTLE], NULL, screen, &dest);
- sprintf(str, "%d", tuxship.lives);
- draw_numbers(str, 10, (screen->h) - 30);
- }
- }
-}
-
-/*Modified from game.c*/
-void FF_draw_led_console(void)
-{
- int i;
- SDL_Rect src, dest;
- int y;
-
- /* draw new console image with "monitor" for LED numbers: */
- draw_console_image(IMG_CONSOLE_LED);
- /* set y to draw LED numbers into Tux's "monitor": */
- y = (screen->h
- - images[IMG_CONSOLE_LED]->h
- + 4); /* "monitor" has 4 pixel margin */
-
- /* begin drawing so as to center display depending on whether minus */
- /* sign needed (4 digit slots) or not (3 digit slots) DSB */
- if (MC_GetOpt(ALLOW_NEGATIVES) )
- dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 4) / 2);
- else
- dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 3) / 2);
-
- for (i = -1; i < MC_MAX_DIGITS; i++) /* -1 is special case to allow minus sign */
- /* with minimal modification of existing code DSB */
- {
- if (-1 == i)
- {
- if (MC_GetOpt(ALLOW_NEGATIVES))
- {
- if (neg_answer_picked)
- src.x = (images[IMG_LED_NEG_SIGN]->w) / 2;
- else
- src.x = 0;
-
- src.y = 0;
- src.w = (images[IMG_LED_NEG_SIGN]->w) / 2;
- src.h = images[IMG_LED_NEG_SIGN]->h;
-
- dest.y = y;
- dest.w = src.w;
- dest.h = src.h;
-
- SDL_BlitSurface(images[IMG_LED_NEG_SIGN], &src, screen, &dest);
- /* move "cursor" */
- dest.x += src.w;
- }
- }
- else
- {
- src.x = digits[i] * ((images[IMG_LEDNUMS]->w) / 10);
- src.y = 0;
- src.w = (images[IMG_LEDNUMS]->w) / 10;
- src.h = images[IMG_LEDNUMS]->h;
-
- /* dest.x already set */
- dest.y = y;
- dest.w = src.w;
- dest.h = src.h;
-
- SDL_BlitSurface(images[IMG_LEDNUMS], &src, screen, &dest);
- /* move "cursor" */
- dest.x += src.w;
- }
- }
-}
-
-/* Draw image at lower center of screen: */
-void draw_console_image(int i)
-{
- SDL_Rect dest;
-
- dest.x = (screen->w - images[i]->w) / 2;
- dest.y = (screen->h - images[i]->h);
- dest.w = images[i]->w;
- dest.h = images[i]->h;
-
- SDL_BlitSurface(images[i], NULL, screen, &dest);
-}
-
-static void FF_draw_bkgr(void)
-{
-
- SDL_BlitSurface(current_bkgd(), NULL, screen, NULL);
- //if(bgSrc.y>bkg_h)
- // SDL_BlitSurface(images[BG_STARS], NULL, screen, &bgScreen);
-
-}
-
-/*Tree rectangle vs a point collitions
- returns 1 if the collitions is detected
- and 0 if not*/
-
-int AsteroidColl(int astW,int astH,int astX,int astY,
- int x, int y)
-{
- int astWq=astW/8;
- int astHq=astH/8;
- int x1, y1, x2, y2;
-
- x1=astX+astWq*3;
- y1=astY;
-
- x2=astX+astWq*6;
- y2=astY+astH;
-
- if(x>x1 && x<x2 && y>y1 && y<y2)
- return 1;
-
- x1=astX;
- y1=astY+astHq*3;
-
- x2=astW;
- y2=astY+astHq*6;
-
- if(x>x1 && x<x2 && y>y1 && y<y2)
- return 1;
-
- x1=astX+astWq;
- y1=astY+astHq;
-
- x2=astX+astWq*7;
- y2=astY+astHq*7;
-
- if(x>x1 && x<x2 && y>y1 && y<y2)
- return 1;
-
- return 0;
-}
-
-// Returns x % w but in the range [-w/2, w/2]
-static int modwrap(int x,int w)
-{
- x = x % w;
- if (x > (w/2))
- x -= w;
- else if (x < -(w/2))
- x += w;
- return x;
-}
-
-static void FF_add_level(void)
-{
- int i = 0;
- int x, y, xvel, yvel, dx, dy;
- int ok;
- int width;
- int safety_radius2, speed2;
- int max_speed;
- Uint32 now_time, last_time;
- SDL_Rect rect;
-
- last_time = now_time = SDL_GetTicks();
-
- wave++;
-
- // New lives per wave!
- if (wave%5==0)
- {
- tuxship.lives++;
- }
-
- //Limit the new asteroids
- if(NUM_ASTEROIDS<MAX_ASTEROIDS)
- NUM_ASTEROIDS=NUM_ASTEROIDS+wave;
- else
- NUM_ASTEROIDS=MAX_ASTEROIDS;
-
- width = screen->w;
- if (screen->h < width)
- width = screen->h;
-
- // Define the "safety radius" as one third of the screen width
- safety_radius2 = width/3;
- safety_radius2 = safety_radius2*safety_radius2; // the square distance
-
- // Define the max speed in terms of the screen width
- max_speed = width/100;
- if (max_speed == 0)
- max_speed = 1;
-
- for (i=0; i<MAX_ASTEROIDS; i++)
- asteroid[i].alive=0;
- for (i=0; i<NUM_ASTEROIDS && NUM_ASTEROIDS<MAX_ASTEROIDS; i++){
- // Generate the new position, avoiding the location of the ship
- ok = 0;
- while (!ok) {
- x = rand()%(screen->w);
- y = rand()%(screen->h);
- dx = modwrap(x - tuxship.x,screen->w);
- dy = modwrap(y - tuxship.y,screen->h);
- if (dx*dx + dy*dy > safety_radius2)
- ok = 1;
- }
- // Generate the new speed, making none of them stationary but none
- // of them too fast
- ok = 0;
- while (!ok) {
- xvel = rand()%(2*max_speed+1) - max_speed;
- yvel = rand()%(2*max_speed+1) - max_speed;
- speed2 = xvel*xvel + yvel*yvel;
- if (speed2 != 0 && speed2 < max_speed*max_speed)
- ok = 1;
- }
- //int FF_add_asteroid(int x, int y, int xspeed, int yspeed, int size, int angle, int angle_speed, int fact_number, int a, int b, int new_wave)
- if(FF_game == FACTOROIDS_GAME){
- FF_add_asteroid(x,y,
- xvel,yvel,
- rand()%2,
- rand()%360, rand()%3,
- (rand()%(31+(wave*wave))),
- 0, 0,
- 1);
- }
- else if(FF_game==FRACTIONS_GAME){
- FF_add_asteroid(x,y,
- xvel,yvel,
- rand()%2,
- rand()%360, rand()%3,
- 0,
- (rand()%(31+(wave*2))), (rand()%(80+(wave*wave))),
- 1);
- }
- }
-
- if(wave != 1)
- {
- while(i < 35)
- {
- i++;
- rect.x=(screen->w/2)-(images[IMG_GOOD]->w/2);
- rect.y=(screen->h/2)-(images[IMG_GOOD]->h/2);
- FF_draw();
- SDL_BlitSurface(images[IMG_GOOD],NULL,screen,&rect);
- SDL_Flip(screen);
-
- last_time = now_time;
- now_time = SDL_GetTicks();
-
- if (now_time < last_time + MS_PER_FRAME)
- {
- now_time = (last_time + MS_PER_FRAME) - now_time; // this holds the delay
- if (now_time > MS_PER_FRAME)
- now_time = MS_PER_FRAME;
- SDL_Delay(now_time);
- }
- }
- }
-}
-
-static int FF_over(int game_status)
-{
- Uint32 last_time, now_time;
- SDL_Rect dest_message;
- SDL_Event event;
-
-
- /* TODO: need better "victory" screen with animation, special music, etc., */
- /* as well as options to review missed questions, play again using missed */
- /* questions as question list, etc. */
- switch (game_status)
- {
- case GAME_OVER_WON:
- {
- int looping = 1;
-// int frame;
- /* set up victory message: */
- dest_message.x = (screen->w - images[IMG_GAMEOVER_WON]->w) / 2;
- dest_message.y = (screen->h - images[IMG_GAMEOVER_WON]->h) / 2;
- dest_message.w = images[IMG_GAMEOVER_WON]->w;
- dest_message.h = images[IMG_GAMEOVER_WON]->h;
-
- do
- {
- //frame++;
- last_time = SDL_GetTicks();
-
- /* draw flashing victory message: */
- //if (((frame / 2) % 4))
- //{
- SDL_BlitSurface(images[IMG_GAMEOVER_WON], NULL, screen, &dest_message);
- //}
-
-
- SDL_Flip(screen);
-
- while (1)
- {
- SDL_PollEvent(&event);
- if (event.type == SDL_QUIT
- || event.type == SDL_KEYDOWN
- || event.type == SDL_MOUSEBUTTONDOWN)
- {
- looping = 0;
- break;
- }
- }
-
- now_time = SDL_GetTicks();
-
- if (now_time < last_time + MS_PER_FRAME)
- SDL_Delay(last_time + MS_PER_FRAME - now_time);
- }
- while (looping);
- break;
- }
-
- case GAME_OVER_ERROR:
- {
- DEBUGMSG(debug_factoroids, "game() exiting with error");
- }
- case GAME_OVER_LOST:
- case GAME_OVER_OTHER:
- {
- int looping = 1;
-
- /* set up GAMEOVER message: */
- dest_message.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
- dest_message.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
- dest_message.w = images[IMG_GAMEOVER]->w;
- dest_message.h = images[IMG_GAMEOVER]->h;
-
- do
- {
- //frame++;
- last_time = SDL_GetTicks();
-
- SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest_message);
- SDL_Flip(screen);
-
- while (1)
- {
- SDL_PollEvent(&event);
- if (event.type == SDL_QUIT
- || event.type == SDL_KEYDOWN
- || event.type == SDL_MOUSEBUTTONDOWN)
- {
- looping = 0;
- break;
- }
- }
-
- now_time = SDL_GetTicks();
-
- if (now_time < last_time + MS_PER_FRAME)
- SDL_Delay(last_time + MS_PER_FRAME - now_time);
- }
- while (looping);
-
- break;
- }
-
- case GAME_OVER_ESCAPE:
- {
- break;
- }
-
- case GAME_OVER_WINDOW_CLOSE:
- {
- break;
- }
-
- }
-
- FF_exit_free();
-
- /* Save score in case needed for high score table: */
- Opts_SetLastScore(score);
-
- /* Return the chosen command: */
- if (GAME_OVER_WINDOW_CLOSE == game_status)
- {
- /* program exits: */
- FF_exit_free();;
- return 1;
- }
- else
- {
- /* return to title() screen: */
- return 0;
- }
-}
-
-
-static void FF_exit_free()
-{
- int i = 0;
-
- free(asteroid);
-
- for(i = 0; i < NUM_OF_ROTO_IMGS; i++)
- {
- if (IMG_tuxship[i])
- {
- SDL_FreeSurface(IMG_tuxship[i]);
- IMG_tuxship[i] = NULL;
- }
- if (IMG_asteroids1[i])
- {
- SDL_FreeSurface(IMG_asteroids1[i]);
- IMG_asteroids1[i] = NULL;
- }
- if (IMG_asteroids2[i])
- {
- SDL_FreeSurface(IMG_asteroids2[i]);
- IMG_asteroids2[i] = NULL;
- }
- }
-
-// SDL_FreeSurface(*IMG_asteroids1);
-// SDL_FreeSurface(*IMG_asteroids2);
-// SDL_FreeSurface(*IMG_tuxship);
-
- if (bkgd)
- {
- SDL_FreeSurface(bkgd);
- bkgd = NULL;
- }
- if (scaled_bkgd)
- {
- SDL_FreeSurface(scaled_bkgd);
- scaled_bkgd = NULL;
- }
-}
-
-/******************* Math Funcs ***********************/
-
-/* Return 1 if the number is prime and 0 if its not */
-int is_prime(int num)
-{
- int i;
- if (num==0 || num==1 || num==-1) return 1;
- else if (num > 0)
- {
-
- for(i = 2; i < num; i++)
- {
- if(num%i == 0) return 0;
- }
- }
- else if (num < 0)
- {
- for(i = 2; i > num; i--)
- {
- if(num%i == 0) return 0;
- }
- }
- return 1;
-}
-
-int is_simplified(int a, int b)
-{
- int i;
- for(i=2; i<1000; i++)
- if(((a%i)==0)&&((b%i)==0))
- return 0;
- return 1;
-}
-/*** Fast cos by Bill***/
-
-int fast_cos(int angle)
-{
- angle = (angle % 45);
-
- if (angle < 12)
- return(trig[angle]);
- else if (angle < 23)
- return(-trig[10 - (angle - 12)]);
- else if (angle < 34)
- return(-trig[angle - 22]);
- else
- return(trig[45 - angle]);
-}
-
-
-/*** Sine based on fast cosine..., by Bill ***/
-
-int fast_sin(int angle)
-{
- return(- fast_cos((angle + 11) % 45));
-}
-
-/******************* LASER FUNCTIONS *********************/
-
-/*Return -1 if no laser is available*/
-int FF_add_laser(void)
-{
- int i, k, zapIndex, zapScore;
- float ux, uy, s, smin,dx,dy,dx2, dy2, d2, thresh;
- int screensize;
- SDL_Surface *asteroid_image;
-
- const float inside_factor = 0.9*0.9;
-
- screensize = screen->w;
- if (screensize < screen->h)
- screensize = screen->h;
-
- for(i=0; i<=MAX_LASER; i++)
- {
- if(laser[i].alive==0)
- {
- // Fire the laser
- laser[i].alive=1;
- laser[i].x=tuxship.centerx;
- laser[i].y=tuxship.centery;
- laser[i].angle=tuxship.angle;
- laser[i].count=15;
-
- ux = cos((float)laser[i].angle * DEG_TO_RAD);
- uy = -sin((float)laser[i].angle * DEG_TO_RAD);
- laser[i].destx = laser[i].x + (int)(ux * screensize);
- laser[i].desty = laser[i].y + (int)(uy * screensize);
-
- // Check to see if it hits asteroids---we only check when it
- // just starts firing, "drift" later doesn't count!
- // We describe the laser path as p = p0 + s*u, where
- // p0 = (x0,y0) is the initial position vector (i.e., the ship)
- // u = (ux,uy) is the unit vector of the laser's direction
- // s (a scalar) is the distance along the laser (s >= 0)
- // With this parametrization, it's easy to calculate the
- // closest approach to the asteroid center, etc.
- zapIndex = -1; // keep track of the closest "hit" asteroid
- zapScore = 0;
- smin = 10*screensize;
-
-
- for (k=0; k<MAX_ASTEROIDS; k++)
- {
- if (!asteroid[k].alive)
- continue;
- asteroid_image = get_asteroid_image(asteroid[k].size,asteroid[k].angle);
- dx = asteroid[k].x + asteroid_image->w/2 - laser[i].x;
- dy = asteroid[k].y + asteroid_image->h/2 - laser[i].y;
- // Find distance along laser of closest approach to asteroid center
- s = dx*ux + dy*uy;
- if (s >= 0) // don't worry about it if it's in the opposite direction! (i.e., behind the ship)
- {
- // Find the distance to the asteroid center at closest approach
- dx2 = dx - s*ux;
- dy2 = dy - s*uy;
- d2 = dx2*dx2 + dy2*dy2;
- thresh = (asteroid_image->h)/2;
- thresh = thresh*thresh*inside_factor;
- if (d2 < thresh)
- {
- // The laser intersects the asteroid. Check to see if
- // the answer works
-
- if( (FF_game==FACTOROIDS_GAME && (asteroid[k].isprime && ((num==asteroid[k].fact_number)||(num==0)))) ||
- (FF_game==FRACTIONS_GAME && (asteroid[k].isprime && num==0))
- )
- {
- // It's valid, check to see if it's closest
- if (s < smin)
- {
- // It's the closest yet examined but has not score
- smin = s;
- zapIndex = k;
- zapScore = 0;
- }
- }
- else if((FF_game==FACTOROIDS_GAME && num > 1 && ((asteroid[k].fact_number%num)==0) && (num!=asteroid[k].fact_number)) ||
- (FF_game==FRACTIONS_GAME && num > 1 && ((asteroid[k].a%num)==0) && ((asteroid[k].b%num)==0) && (num!=asteroid[k].fact_number)))
- {
- // It's valid, check to see if it's closest
- if (s < smin)
- {
- // It's the closest yet examined and has socre
- smin = s;
- zapIndex = k;
- zapScore = 1;
- }
- }
- }
- }
- }
-
- // Handle the destruction, score, and extra lives
- if (zapIndex >= 0) // did we zap one?
- {
- isdead = 1;
- laser[i].destx = laser[i].x + (int)(ux * smin);
- laser[i].desty = laser[i].y + (int)(uy * smin);
- FF_destroy_asteroid(zapIndex,2*ux,2*uy);
- playsound(SND_SIZZLE);
-
- if (floor((float)score/100) < floor((float)(score+num)/100))
- tuxship.lives++;
- if(zapScore)
- {
- score += num;
- }
- }
- return 1;
- }
- }
- fprintf(stderr, "Laser could't be created!\n");
- return -1;
-}
-
-/******************* ASTEROIDS FUNCTIONS *******************/
-
-
-
-static int FF_add_asteroid(int x, int y, int xspeed, int yspeed, int size, int angle, int angle_speed, int fact_number, int a, int b, int new_wave)
-{
- int i;
- for(i=0; i<MAX_ASTEROIDS; i++){
- if(asteroid[i].alive==0)
- {
- asteroid[i].alive=1;
- asteroid[i].rx=x;
- asteroid[i].ry=y;
- asteroid[i].angle=angle;
- asteroid[i].angle_speed=angle_speed;
- asteroid[i].y=(asteroid[i].ry - (IMG_tuxship[asteroid[i].angle/DEG_PER_ROTATION]->h/2));
- asteroid[i].x=(asteroid[i].rx - (IMG_tuxship[asteroid[i].angle/DEG_PER_ROTATION]->w/2));
- asteroid[i].yspeed=yspeed;
- asteroid[i].xspeed=xspeed;
-
- if(FF_game==FACTOROIDS_GAME){
-
- asteroid[i].fact_number=fact_number;
-
- while(!asteroid[i].fact_number)
- asteroid[i].fact_number=rand()%80;
-
- asteroid[i].isprime=is_prime(asteroid[i].fact_number);
-
- }else if(FF_game==FRACTIONS_GAME){
-
- asteroid[i].a=a;
- asteroid[i].b=b;
-
- while(!asteroid[i].a)
- asteroid[i].a=rand()%80;
- while(!asteroid[i].b)
- asteroid[i].b=rand()%80;
-
- asteroid[i].isprime=is_simplified(asteroid[i].a,asteroid[i].b);
- }
-
- if(new_wave){
- if(tuxship.x-50<asteroid[i].x+80 &&
- tuxship.x+50>asteroid[i].x &&
- tuxship.y-50<asteroid[i].y+80 &&
- tuxship.y+50>asteroid[i].y &&
- tuxship.lives>0 &&
- asteroid[i].alive){
- asteroid[i].rx=asteroid[i].rx+300;
- asteroid[i].ry=asteroid[i].ry+300;
- }
- }
-
- if(asteroid[i].isprime)
- {
- asteroid[i].size=0;
- asteroid[i].centerx=(images[IMG_ASTEROID1]->w/2)+asteroid[i].x;
- asteroid[i].centery=(images[IMG_ASTEROID1]->h/2)+asteroid[i].y;
- asteroid[i].radius=(images[IMG_ASTEROID1]->h/2);
-
- }
- else if(!asteroid[i].isprime)
- {
- asteroid[i].size=1;
- asteroid[i].centerx=(images[IMG_ASTEROID2]->w/2)+asteroid[i].x;
- asteroid[i].centery=(images[IMG_ASTEROID2]->h/2)+asteroid[i].y;
- asteroid[i].radius=(images[IMG_ASTEROID1]->h/2);
- }
-
- while (asteroid[i].xspeed==0)
- {
- asteroid[i].xspeed = ((rand() % 3) - 1)*2;
- }
- return 1;
- }
- }
- fprintf(stderr, "Asteroid could't be created!\n");
- return -1;
-}
-
-int FF_destroy_asteroid(int i, float xspeed, float yspeed)
-{
- if(asteroid[i].alive==1){
- isdead=1;
- xdead=asteroid[i].x;
- ydead=asteroid[i].y;
- if(asteroid[i].size>0){
- /* Break the rock into two smaller ones! */
- if(num!=0){
-
-
-//static int FF_add_asteroid(int x, int y, int xspeed, int yspeed, int size, int angle, int
-// angle_speed, int fact_number, int a, int b, int new_wave
-
- if(FF_game==FACTOROIDS_GAME){
- FF_add_asteroid(asteroid[i].rx,
- asteroid[i].ry,
- asteroid[i].xspeed + (xspeed - yspeed)/2,
- asteroid[i].yspeed + (yspeed + xspeed)/2,
- 0,
- rand()%360, rand()%3, (int)(asteroid[i].fact_number/num),
- 0, 0,
- 0);
-
- FF_add_asteroid(asteroid[i].rx,
- asteroid[i].ry,
- asteroid[i].xspeed + (xspeed + yspeed)/2,
- asteroid[i].yspeed + (yspeed - xspeed)/2,
- 0,
- rand()%360, rand()%3, num,
- 0, 0,
- 0);
- }
- else if(FF_game==FRACTIONS_GAME){
- FF_add_asteroid(asteroid[i].rx,
- asteroid[i].ry,
- ((asteroid[i].xspeed + xspeed) / 2),
- (asteroid[i].yspeed + yspeed),
- 0,
- rand()%360, rand()%3, 0,
- (int)(asteroid[i].a/num), (int)(asteroid[i].b/num),
- 0);
-
- FF_add_asteroid(asteroid[i].rx,
- asteroid[i].ry,
- (asteroid[i].xspeed + xspeed),
- ((asteroid[i].yspeed + yspeed) / 2),
- 0,
- rand()%360, rand()%3, 0,
- (int)(asteroid[i].b/num), (int)(asteroid[i].a/num),
- 0);
- }
- }
- }
-
- /* Destroy the old asteroid */
-
- asteroid[i].alive=0;
- return 1;
- }
- return 0;
-}
-
-/************** MODIFIED FUNCS FROM game.c and titlescreen.c ******************/
-
-void FF_ShowMessage(char* str1, char* str2, char* str3, char* str4)
-{
- SDL_Surface *s1, *s2, *s3, *s4;
- SDL_Rect loc;
-
- s1 = s2 = s3 = s4 = NULL;
-
- DEBUGMSG(debug_factoroids, "ShowMessage() - creating text\n" );
-
- if (str1)
- s1 = BlackOutline(str1, DEFAULT_MENU_FONT_SIZE, &white);
- if (str2)
- s2 = BlackOutline(str2, DEFAULT_MENU_FONT_SIZE, &white);
- if (str3)
- s3 = BlackOutline(str3, DEFAULT_MENU_FONT_SIZE, &white);
- /* When we get going with i18n may need to modify following - see below: */
- if (str4)
- s4 = BlackOutline(str4, DEFAULT_MENU_FONT_SIZE, &white);
-
- DEBUGMSG(debug_factoroids, "ShowMessage() - drawing screen\n" );
-
- /* Draw lines of text (do after drawing Tux so text is in front): */
- if (s1)
- {
- loc.x = (screen->w / 2) - (s1->w/2);
- loc.y = (screen->h / 2) + 10;
- SDL_BlitSurface( s1, NULL, screen, &loc);
- }
- if (s2)
- {
- loc.x = (screen->w / 2) - (s2->w/2);
- loc.y = (screen->h / 2) + 80;
- SDL_BlitSurface( s2, NULL, screen, &loc);
- }
- if (s3)
- {
- loc.x = (screen->w / 2) - (s3->w/2);
- loc.y = (screen->h / 2) + 130;
- SDL_BlitSurface( s3, NULL, screen, &loc);
- }
- if (s4)
- {
- loc.x = (screen->w / 2) - (s4->w/2);
- loc.y = (screen->h / 2) + 180;
- SDL_BlitSurface( s4, NULL, screen, &loc);
- }
-
- /* and update: */
- SDL_UpdateRect(screen, 0, 0, 0, 0);
-
-
- SDL_FreeSurface(s1);
- SDL_FreeSurface(s2);
- SDL_FreeSurface(s3);
- SDL_FreeSurface(s4);
-}
-
-
-void game_handle_user_events(void)
-{
- SDL_Event event;
- SDLKey key;
-
- while (SDL_PollEvent(&event) > 0)
- {
- if (event.type == SDL_QUIT)
- {
- SDL_quit_received = 1;
- quit = 1;
- }
- if (event.type == SDL_MOUSEBUTTONDOWN)
- {
- key = game_mouse_event(event);
- }
- if (event.type == SDL_KEYDOWN ||
- event.type == SDL_KEYUP)
- {
- key = event.key.keysym.sym;
-
- if (event.type == SDL_KEYDOWN)
- {
- if (key == SDLK_ESCAPE)
- {
- // Return to menu!
- escape_received = 1;
-
- }
-
- // Key press...
-
- if (key == SDLK_RIGHT)
- {
- // Rotate CW
-
- left_pressed = 0;
- right_pressed = 1;
- }
- else if (key == SDLK_LEFT)
- {
- // Rotate CCW
-
- left_pressed = 1;
- right_pressed = 0;
- }
- else if (key == SDLK_UP)
- {
- // Thrust!
-
- up_pressed = 1;
- }
-
- if (key == SDLK_LSHIFT || key == SDLK_RSHIFT)
- {
- // Respawn now (if applicable)
- shift_pressed = 1;
- }
-
- if (key == SDLK_TAB || key == SDLK_p)
- {
- /* [TAB] or [P]: Pause! (if settings allow) */
- if (Opts_AllowPause())
- {
- paused = 1;
- }
- }
- /* The rest of the keys control the numeric answer console: */
-
- if (key >= SDLK_0 && key <= SDLK_9)
- {
- /* [0]-[9]: Add a new digit: */
- digits[0] = digits[1];
- digits[1] = digits[2];
- digits[2] = key - SDLK_0;
- tux_pressing = 1;
- playsound(SND_SHIELDSDOWN);
- }
- 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] = key - SDLK_KP0;
- tux_pressing = 1;
- playsound(SND_SHIELDSDOWN);
- }
- /* support for negative answer input DSB */
- else if ((key == SDLK_MINUS || key == SDLK_KP_MINUS))
- //&& MC_AllowNegatives()) /* do nothing unless neg answers allowed */
- {
- /* allow player to make answer negative: */
- neg_answer_picked = 1;
- tux_pressing = 1;
- playsound(SND_SHIELDSDOWN);
- }
- else if ((key == SDLK_PLUS || key == SDLK_KP_PLUS))
- //&& MC_AllowNegatives()) /* do nothing unless neg answers allowed */
- {
- /* allow player to make answer positive: */
- neg_answer_picked = 0;
- tux_pressing = 1;
- playsound(SND_SHIELDSDOWN);
- }
- else if (key == SDLK_BACKSPACE ||
- key == SDLK_CLEAR ||
- key == SDLK_DELETE)
- {
- /* [BKSP]: Clear digits! */
- digits[0] = 0;
- digits[1] = 0;
- digits[2] = 0;
- tux_pressing = 1;
- playsound(SND_SHIELDSDOWN);
- }
- else if (key == SDLK_RETURN ||
- key == SDLK_KP_ENTER ||
- key == SDLK_SPACE)
- {
- shoot_pressed = 1;
- doing_answer = 1;
- playsound(SND_LASER);
- }
-
-
- }
- else if (event.type == SDL_KEYUP)
- {
- // Key release...
-
- if (key == SDLK_RIGHT)
- {
- right_pressed = 0;
- }
- else if (key == SDLK_LEFT)
- {
- left_pressed = 0;
- }
- else if (key == SDLK_UP)
- {
- up_pressed = 0;
- }
- if (key == SDLK_LSHIFT ||
- key == SDLK_RSHIFT)
- {
- // Respawn now (if applicable)
- shift_pressed = 0;
- }
- }
- }
-
-#ifdef JOY_YES
- else if (event.type == SDL_JOYBUTTONDOWN &&
- player_alive)
- {
- if (event.jbutton.button == JOY_B)
- {
- shoot_pressed = 1;
- }
- else if (event.jbutton.button == JOY_A)
- {
- // Thrust:
-
- up_pressed = 1;
- }
- else
- {
- shift_pressed = 1;
- }
- }
- else if (event.type == SDL_JOYBUTTONUP)
- {
- if (event.jbutton.button == JOY_A)
- {
- // Stop thrust:
-
- up_pressed = 0;
- }
- else if (event.jbutton.button != JOY_B)
- {
- shift_pressed = 0;
- }
- }
- else if (event.type == SDL_JOYAXISMOTION)
- {
- if (event.jaxis.axis == JOY_X)
- {
- if (event.jaxis.value < -256)
- {
- left_pressed = 1;
- right_pressed = 0;
- }
- else if (event.jaxis.value > 256)
- {
- left_pressed = 0;
- right_pressed = 1;
- }
- else
- {
- left_pressed = 0;
- right_pressed = 0;
- }
- }
- }
-#endif
-
- }
-
-}
-
-static int game_mouse_event(SDL_Event event)
-{
- int keypad_w, keypad_h, x, y, row, column;
- SDLKey key = SDLK_UNKNOWN;
-
- keypad_w = 0;
- keypad_h = 0;
-
- /* Check to see if user clicked exit button: */
- /* The exit button is in the upper right corner of the screen: */
- if ((event.button.x >= (screen->w - images[IMG_STOP]->w))
- &&(event.button.y <= images[IMG_STOP]->h))
- {
- key = SDLK_ESCAPE;
- //game_key_event(key);
- escape_received = 1;
- quit = 1;
- return -1;
- }
-
- /* get out unless we really are using keypad */
- if ( level_start_wait
- || Opts_DemoMode()
- || !Opts_GetGlobalOpt(USE_KEYPAD))
- {
- return -1;
- }
-
-
- /* 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 (1)//MC_AllowNegatives())
- {
- if (!images[IMG_KEYPAD])
- return -1;
- else
- {
- keypad_w = images[IMG_KEYPAD]->w;
- keypad_h = images[IMG_KEYPAD]->h;
- }
- }
- else
- {
- if (!images[IMG_KEYPAD_NO_NEG])
- return -1;
- else
- {
- keypad_w = images[IMG_KEYPAD]->w;
- keypad_h = images[IMG_KEYPAD]->h;
- }
- }
-
- if (!keypad_w || !keypad_h)
- {
- return -1;
- }
-
-
- /* only proceed if click falls within keypad: */
- if (!((event.button.x >=
- (screen->w / 2) - (keypad_w / 2) &&
- event.button.x <=
- (screen->w / 2) + (keypad_w / 2) &&
- event.button.y >=
- (screen->h / 2) - (keypad_h / 2) &&
- event.button.y <=
- (screen->h / 2) + (keypad_h / 2))))
- /* click outside of keypad - do nothing */
- {
- return -1;
- }
-
- else /* click was within keypad */
- {
- 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 */
- /* */
- /* The on-screen keypad has a 4 x 4 layout: */
- /* */
- /* ********************************* */
- /* * * * * * */
- /* * 7 * 8 * 9 * - * */
- /* * * * * * */
- /* ********************************* */
- /* * * * * * */
- /* * 4 * 5 * 6 * * */
- /* * * * * * */
- /* ************************* + * */
- /* * * * * * */
- /* * 1 * 2 * 3 * * */
- /* * * * * * */
- /* ********************************* */
- /* * * * */
- /* * 0 * Enter * */
- /* * * * */
- /* ********************************* */
- /* */
- /* The following code simply figures out the */
- /* row and column based on x and y and looks */
- /* up the SDlKey accordingly. */
-
- column = x/((keypad_w)/4);
- row = y/((keypad_h)/4);
-
- /* make sure row and column are sane */
- if (column < 0
- || column > 3
- || row < 0
- || row > 3)
- {
- printf("\nIllegal row or column value!\n");
- return -1;
- }
-
- /* simple but tedious - I am sure this could be done more elegantly */
-
- if (0 == row)
- {
- if (0 == column)
- key = SDLK_7;
- if (1 == column)
- key = SDLK_8;
- if (2 == column)
- key = SDLK_9;
- if (3 == column)
- key = SDLK_MINUS;
- }
- if (1 == row)
- {
- if (0 == column)
- key = SDLK_4;
- if (1 == column)
- key = SDLK_5;
- if (2 == column)
- key = SDLK_6;
- if (3 == column)
- key = SDLK_PLUS;
- }
- if (2 == row)
- {
- if (0 == column)
- key = SDLK_1;
- if (1 == column)
- key = SDLK_2;
- if (2 == column)
- key = SDLK_3;
- if (3 == column)
- key = SDLK_PLUS;
- }
- if (3 == row)
- {
- if (0 == column)
- key = SDLK_0;
- if (1 == column)
- key = SDLK_RETURN;
- if (2 == column)
- key = SDLK_RETURN;
- if (3 == column)
- key = SDLK_RETURN;
- }
-
- if (key == SDLK_UNKNOWN)
- {
- return -1;
- }
-
- /* now can proceed as if keyboard was used */
- //game_key_event(key);
- return key;
- }
-}
-
-
-static int check_exit_conditions(void)
-{
- if(SDL_quit_received)
- {
- return GAME_OVER_WINDOW_CLOSE;
- }
-
- if(escape_received)
- {
- return GAME_OVER_ESCAPE;
- }
- if(tuxship.lives<=0)
- {
- return GAME_OVER_LOST;
- }
- if(score>=19999 || wave >= 20 )
- {
- return GAME_OVER_WON;
- }
- /* determine if game lost (i.e. all cities blown up): */
- /*if (!num_cities_alive)
- {
- if (gameover_counter < 0)
- gameover_counter = GAMEOVER_COUNTER_START;
- gameover_counter--;
- if (gameover_counter == 0)
- return GAME_OVER_LOST;
- }*/
-
- /* determine if game won (i.e. all questions in mission answered correctly): */
- /*if (MC_MissionAccomplished())
- {
- return GAME_OVER_WON;
- }*/
-
- /* Could have situation where mathcards doesn't have more questions */
- /* even though not all questions answered correctly: */
- /*if (!MC_TotalQuestionsLeft())
- {
- return GAME_OVER_OTHER;
- }*/
-
- /* Need to get out if no comets alive and MathCards has no questions left in list, */
- /* even though MathCards thinks there are still questions "in play". */
- /* This SHOULD NOT HAPPEN and means we have a bug somewhere. */
- /* if (!MC_ListQuestionsLeft() && !num_comets_alive)
- {
- return GAME_OVER_ERROR;
- }
- */
- /* If using demo mode, see if counter has run out: */
- /*if (Opts_DemoMode())
- {
- if (demo_countdown <= 0 )
- return GAME_OVER_OTHER;
- }*/
-
- /* if we made it to here, the game goes on! */
- return GAME_IN_PROGRESS;
-}
diff --git a/src/factoroids.h b/src/factoroids.h
deleted file mode 100644
index aa5c76d..0000000
--- a/src/factoroids.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/************************************************************
- * factoroids.h *
- * *
- * Description: Contains headers for the fractor and *
- * fraction activities. *
- * *
- * Autor: Jesus M. Mager H. (fongog at gmail.com) 2008 *
- * Copyright: GPL v3 or later *
- * *
- * TuxMath *
- * Part of "Tux4Kids" Project *
- * http://tux4kids.alioth.debian.org/ *
- ************************************************************/
-
-#ifndef FACTOROIDS_H
-#define FACTOROIDS_H
-
-// Used in titleecreen.c
-
-int factors(void);
-int fractions(void);
-
-#endif
diff --git a/src/generate_lesson.c b/src/generate_lesson.c
deleted file mode 100644
index 50fb389..0000000
--- a/src/generate_lesson.c
+++ /dev/null
@@ -1,65 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "globals.h"
-#include "options.h"
-#include "mathcards.h"
-#include "fileops.h"
-
-/* Compile this with the following statement:
-
-gcc -lm -o generate_lesson -DDATA_PREFIX=\"/usr/local/share/tuxmath\" generate_lesson.c mathcards.c options.c fileops.c lesson.c
-
-Usage: generate_lesson configfile1 configfile2 ...
-
-*/
-
-/* Declarations needed for the auxillary functions */
-char **lesson_list_titles = NULL;
-char **lesson_list_filenames = NULL;
-int num_lessons = 0;
-
-int read_high_scores_fp(FILE* fp)
-{
- /* This is a stub to let things compile */
- return 1;
-}
-
-void initialize_scores(void)
-{
- /* This is a stub to let things compile */
-}
-
-int main(int argc,char *argv[])
-{
- int i;
-
- /* Initialize MathCards backend for math questions: */
- if (!MC_Initialize())
- {
- printf("\nUnable to initialize MathCards\n");
- fprintf(stderr, "\nUnable to initialize MathCards\n");
- exit(1);
- }
-
- /* initialize game_options struct with defaults DSB */
- if (!Opts_Initialize())
- {
- fprintf(stderr, "\nUnable to initialize game_options\n");
- exit(1);
- }
-
- /* This next bit allows multiple config files to be read in sequence, since
- this is something that happens in the ordinary course of events
- in tuxmath itself. */
- for (i = 1; i < argc; i++) {
- printf("Reading %s\n",argv[i]);
- read_named_config_file(argv[i]);
- }
- printf("All done reading!\n");
-
- MC_StartGame();
- MC_PrintQuestionList(stdout);
- return 0;
-}
diff --git a/src/mathcards.c b/src/mathcards.c
deleted file mode 100644
index 2b17564..0000000
--- a/src/mathcards.c
+++ /dev/null
@@ -1,2550 +0,0 @@
-/*
-* C Implementation: mathcards.c
-*
-* Description: implementation of backend for a flashcard-type math game.
- Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
- (aka tuxmath). (If tuxmath were a C++ program, this would be a C++ class).
- MathCards could be used as the basis for similar games using a different interface.
-
-*
-*
-* Author: David Bruce <davidstuartbruce at gmail.com>, (C) 2005
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*
-* Revised extensively in 2008 by Brendan Luchen, Tim Holy, and David Bruce
-* Revised more in 2009 by David Bruce
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-
-
-#include "transtruct.h"
-#include "mathcards.h"
-#include "globals.h"
-
-/* extern'd constants */
-
-const char* const MC_OPTION_TEXT[NOPTS+1] = {
-"PLAY_THROUGH_LIST",
-"QUESTION_COPIES",
-"REPEAT_WRONGS",
-"COPIES_REPEATED_WRONGS",
-"ALLOW_NEGATIVES",
-"MAX_ANSWER",
-"MAX_QUESTIONS",
-"MAX_FORMULA_NUMS",
-"MIN_FORMULA_NUMS",
-
-"FORMAT_ANSWER_LAST",
-"FORMAT_ANSWER_FIRST",
-"FORMAT_ANSWER_MIDDLE",
-"FORMAT_ADD_ANSWER_LAST",
-"FORMAT_ADD_ANSWER_FIRST",
-"FORMAT_ADD_ANSWER_MIDDLE",
-"FORMAT_SUB_ANSWER_LAST",
-"FORMAT_SUB_ANSWER_FIRST",
-"FORMAT_SUB_ANSWER_MIDDLE",
-"FORMAT_MULT_ANSWER_LAST",
-"FORMAT_MULT_ANSWER_FIRST",
-"FORMAT_MULT_ANSWER_MIDDLE",
-"FORMAT_DIV_ANSWER_LAST",
-"FORMAT_DIV_ANSWER_FIRST",
-"FORMAT_DIV_ANSWER_MIDDLE",
-
-"ADDITION_ALLOWED",
-"SUBTRACTION_ALLOWED",
-"MULTIPLICATION_ALLOWED",
-"DIVISION_ALLOWED",
-"TYPING_PRACTICE_ALLOWED",
-"ARITHMETIC_ALLOWED",
-"COMPARISON_ALLOWED",
-
-"MIN_AUGEND",
-"MAX_AUGEND",
-"MIN_ADDEND",
-"MAX_ADDEND",
-
-"MIN_MINUEND",
-"MAX_MINUEND",
-"MIN_SUBTRAHEND",
-"MAX_SUBTRAHEND",
-
-"MIN_MULTIPLIER",
-"MAX_MULTIPLIER",
-"MIN_MULTIPLICAND",
-"MAX_MULTIPLICAND",
-
-"MIN_DIVISOR",
-"MAX_DIVISOR",
-"MIN_QUOTIENT",
-"MAX_QUOTIENT",
-
-"MIN_TYPING_NUM",
-"MAX_TYPING_NUM",
-
-"MIN_COMPARATOR" ,
-"MAX_COMPARATOR" ,
-"MIN_COMPARISAND",
-"MAX_COMPARISAND",
-
-"RANDOMIZE",
-
-"COMPREHENSIVE",
-"AVG_LIST_LENGTH",
-"VARY_LIST_LENGTH",
-
-"END_OF_OPTS"
-};
-
-
-
-const int MC_DEFAULTS[] = {
- 1, //PLAY_THROUGH_LIST
- 1, //QUESTION_COPIES
- 1, //REPEAT_WRONGS
- 1, //COPIES_REPEATED_WRONGS
- 0, //ALLOW_NEGATIVES
- 999, //MAX_ANSWER
- 5000, //MAX_QUESTIONS
- 2, //MAX_FORMULA_NUMS
- 2, //MIN_FORMULA_NUMS
- //
- 1, //FORMAT_ANSWER_LAST
- 0, //FORMAT_ANSWER_FIRST
- 0, //FORMAT_ANSWER_MIDDLE
- 1, //FORMAT_ADD_ANSWER_LAST
- 0, //FORMAT_ADD_ANSWER_FIRST
- 0, //FORMAT_ADD_ANSWER_MIDDLE
- 1, //FORMAT_SUB_ANSWER_LAST
- 0, //FORMAT_SUB_ANSWER_FIRST
- 0, //FORMAT_SUB_ANSWER_MIDDLE
- 1, //FORMAT_MULT_ANSWER_LAST
- 0, //FORMAT_MULT_ANSWER_FIRST
- 0, //FORMAT_MULT_ANSWER_MIDDLE
- 1, //FORMAT_DIV_ANSWER_LAST
- 0, //FORMAT_DIV_ANSWER_FIRST
- 0, //FORMAT_DIV_ANSWER_MIDDLE
- //
- 1, //ADDITION_ALLOWED
- 1, //SUBTRACTION_ALLOWED
- 1, //MULTIPLICATION_ALLOWED
- 1, //DIVISION_ALLOWED
-
- 0, //TYPING_PRACTICE_ALLOWED
- 1, //ARITHMETIC_ALLOWED
- 0, //COMPARISON_ALLOWED
- //
- 0, //MIN_AUGEND
- 12, //MAX_AUGEND
- 0, //MIN_ADDEND
- 12, //MAX_ADDEND
- //
- 0, //MIN_MINUEND
- 12, //MAX_MINUEND
- 0, //MIN_SUBTRAHEND
- 12, //MAX_SUBTRAHEND
- //
- 0, //MIN_MULTIPLIER
- 12, //MAX_MULTIPLIER
- 0, //MIN_MULTIPLICAND
- 12, //MAX_MULTIPLICAND
- //
- 0, //MIN_DIVISOR
- 12, //MAX_DIVISOR
- 0, //MIN_QUOTIENT
- 12, //MAX_QUOTIENT
- //
- 0, //MIN_TYPING_NUM
- 12, //MAX_TYPING_NUM
- //
- 0, //MIN_COMPARATOR
- 12, //MAX_COMPARATOR
- 0, //MIN_COMPARISAND
- 12, //MAX_COMPARISAND
-
- 1, //RANDOMIZE
-
- 0, //COMPREHENSIVE
-
- //NOTE if "comprehensive" is off, we randomly generate a set of
- //"avg list length" questions one by one. If "vary list length"
- //is on, that length is also randomized somewhat
- 100, //AVG_LIST_LENGTH
- 0 //VARY_LIST_LENGTH
-};
-
-
-
-/* "Globals" for mathcards.c: */
-#define PI_VAL 3.1415927
-#define NPRIMES 9
-const int smallprimes[NPRIMES] = {2, 3, 5 ,7, 11, 13, 17, 19, 23};
-const char operchars[4] = "+-*/";
-
-MC_Options* math_opts = NULL;
-MC_MathQuestion* question_list = NULL;
-MC_MathQuestion* wrong_quests = NULL;
-MC_MathQuestion* active_quests = NULL;
-MC_MathQuestion* next_wrong_quest = NULL;
-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;
-static int id = 0;
-
-/* For keeping track of timing data */
-float* time_per_question_list = NULL;
-int length_time_per_question_list = 0;
-int length_alloc_time_per_question_list = 0;
-
-const MC_FlashCard DEFAULT_CARD = {{'\0'}, {'\0'}, 0, 0, 0}; //empty card to signal error
-
-/* "private" function prototypes: */
-/* */
-/* these are for internal use by MathCards only - like */
-/* the private functions of a C++ class. Declared static */
-/* to give file scope rather than extern scope. */
-
-static MC_MathQuestion* generate_list(void);
-static void clear_negatives(void);
-//static int validate_question(int n1, int n2, int n3);
-//static MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f);
-//static MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard);
-static MC_MathQuestion* create_node_copy(MC_MathQuestion* other);
-static MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node);
-static MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node);
-static MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n);
-static MC_MathQuestion* delete_list(MC_MathQuestion* list);
-//static int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy);
-static int list_length(MC_MathQuestion* list);
-static int randomize_list(MC_MathQuestion** list);
-
-int comp_randomizer(const void* a, const void* b);
-static MC_MathQuestion* pick_random(int length, MC_MathQuestion* list);
-static int compare_node(MC_MathQuestion* first, MC_MathQuestion* other);
-static int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr);
-//static int int_to_bool(int i);
-//static int sane_value(int i);
-//static int abs_value(int i);
-static int floatCompare(const void* v1,const void* v2);
-
-static void print_list(FILE* fp,MC_MathQuestion* list);
-void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length);
-
-static void print_counters(void);
-//static MC_FlashCard create_card_from_node(MC_MathQuestion* node);
-
-
-/* Functions for new mathcards architecture */
-static void free_node(MC_MathQuestion* mq); //wrapper for free() that also frees card
-static MC_FlashCard generate_random_flashcard(void);
-static MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat);
-static MC_MathQuestion* allocate_node(void); //allocate space for a node
-static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b); //test for identical cards
-static int find_divisor(int a); //return a random positive divisor of a
-static int calc_num_valid_questions(void);
-static MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list);
-static MC_MathQuestion* find_node(MC_MathQuestion* list, int num);
-//Determine how many points to give player based on question
-//difficulty and how fast it was answered.
-//TODO we may want to play with this a bit
-static int calc_score(int difficulty, float t);
-
-
-
-
-
-
-/* MC_Initialize() sets up the struct containing all of */
-/* settings regarding math questions. It should be */
-/* called before any other function. Many of the other */
-/* functions will not work properly if MC_Initialize() */
-/* has not been called. It only needs to be called once, */
-/* i.e when the program is starting, not at the beginning*/
-/* of each math game for the player. Returns 1 if */
-/* successful, 0 otherwise. */
-int MC_Initialize(void)
-{
- int i;
-
- DEBUGMSG(debug_mathcards,"\nEntering MC_Initialize()");
- /* check flag to see if we did this already */
- if (initialized)
- {
-
- DEBUGCODE(debug_mathcards)
- {
- printf("\nAlready initialized");
- MC_PrintMathOptions(stdout, 0);
- printf("\nLeaving MC_Initialize()\n");
- }
-
- return 1;
- }
- math_opts = malloc(sizeof(MC_Options));
- /* bail out if no struct */
- if (!math_opts)
- {
- DEBUGMSG(debug_mathcards,"\nError: malloc couldn't allocate math_opts for some reason\n");
- DEBUGMSG(debug_mathcards,"\nLeaving MC_Initialize()\n");
-
- fprintf(stderr, "\nUnable to initialize math_options");
- return 0;
- }
-
- /* set defaults */
- for (i = 0; i < NOPTS; ++i)
- {
- math_opts->iopts[i] = MC_DEFAULTS[i];
- }
-
- /* if no negatives to be used, reset any negatives to 0 */
- if (!math_opts->iopts[ALLOW_NEGATIVES])
- {
- clear_negatives();
- }
-
- initialized = 1;
-
- DEBUGCODE(debug_mathcards)
- {
- MC_PrintMathOptions(stdout, 0);
- printf("\nLeaving MC_Initialize()\n");
- }
-
- 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)
-{
-
- DEBUGMSG(debug_mathcards,"\nEntering MC_StartGame()");
-
- /* if math_opts not set up yet, initialize it: */
- if (!initialized)
- {
- DEBUGMSG(debug_mathcards, "\nNot initialized - calling MC_Initialize()");
- MC_Initialize();
- }
-
- if (!math_opts)
- {
- DEBUGMSG(debug_mathcards, "\nCould not initialize - bailing out");
- DEBUGMSG(debug_mathcards, "\nLeaving MC_StartGame()\n");
- return 0;
- }
-
- /* we know math_opts exists if we make it to here */
- srand(time(NULL));
-
- /* clear out old lists if starting another game: (if not done already) */
- delete_list(question_list);
- question_list = NULL;
- delete_list(wrong_quests);
- wrong_quests = NULL;
- delete_list(active_quests);
- active_quests = NULL;
-
- /* clear the time list */
- if (time_per_question_list != NULL)
- {
- free(time_per_question_list);
- time_per_question_list = NULL;
- length_time_per_question_list = 0;
- length_alloc_time_per_question_list = 0;
- }
-
- question_list = generate_list();
- next_wrong_quest = NULL;
- /* 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;
-
- if (debug_status & debug_mathcards) {
- print_counters();
- }
-
-/* make sure list now exists and has non-zero length: */
- if (question_list && quest_list_length)
- {
- DEBUGMSG(debug_mathcards, "\nGame set up successfully");
- DEBUGMSG(debug_mathcards, "\nLeaving MC_StartGame()\n");
-
- return 1;
- }
- else
- {
- DEBUGMSG(debug_mathcards, "\nGame NOT set up successfully - no valid list");
- DEBUGMSG(debug_mathcards, "\nLeaving MC_StartGame()\n");
-
- return 0;
- }
-}
-
-/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
-/* but uses the incorrectly answered questions from the */
-/* previous game for the question list as a review form */
-/* of learning. If there were no wrong answers (or no */
-/* previous game), it behaves just like MC_StartGame(). */
-/* FIXME wonder if it should return a different value if */
-/* the list is created from settings because there is no */
-/* valid wrong question list? */
-int MC_StartGameUsingWrongs(void)
-{
- DEBUGMSG(debug_mathcards, "\nEntering MC_StartGameUsingWrongs()");
-
- /* Note: if not initialized, control will pass to */
- /* MC_StartGame() via else clause so don't need to test */
- /* for initialization here */
- if (wrong_quests &&
- list_length(wrong_quests))
- {
- DEBUGMSG(debug_mathcards, "\nNon-zero length wrong_quests list found, will");
- DEBUGMSG(debug_mathcards, "\nuse for new game list:");
-
- /* initialize lists for new game: */
- delete_list(question_list);
- if(!randomize_list(&wrong_quests))
- {
- fprintf(stderr, "Error during randomization of wrong_quests!\n");
- /* Punt on trying wrong question list, just run normal game */
- return MC_StartGame();
- }
- question_list = wrong_quests;
- wrong_quests = 0;
- next_wrong_quest = 0;
- delete_list(active_quests);
- active_quests = 0;
- /* initialize counters for new game: */
- quest_list_length = list_length(question_list);
- unanswered = starting_length = quest_list_length;
- answered_correctly = 0;
- answered_wrong = 0;
- questions_pending = 0;
-
- if (debug_status & debug_mathcards) {
- print_counters();
- print_list(stdout, question_list);
- printf("\nLeaving MC_StartGameUsingWrongs()\n");
- }
-
- return 1;
- }
- else /* if no wrong_quests list, go to MC_StartGame() */
- /* to set up list based on math_opts */
- {
- DEBUGMSG(debug_mathcards, "\nNo wrong questions to review - generate list from math_opts\n");
- DEBUGMSG(debug_mathcards, "\nLeaving MC_StartGameUsingWrongs()\n");
-
- return MC_StartGame();
- }
-}
-
-
-/* MC_NextQuestion() takes a pointer to an allocated */
-/* MC_FlashCard 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)
-{
- DEBUGMSG(debug_mathcards, "\nEntering MC_NextQuestion()\n");
-
- /* (so we can move the node into active_quests:) */
- MC_MathQuestion* ptr;
-
- if (!fc )
- {
- fprintf(stderr, "\nNull MC_FlashCard* argument!\n");
- DEBUGMSG(debug_mathcards, "\nLeaving MC_NextQuestion()\n");
- return 0;
- }
-
- if (!question_list ||
-/* !next_question || */
- !list_length(question_list) )
- {
- DEBUGMSG(debug_mathcards, "\nquestion_list invalid or empty");
- DEBUGMSG(debug_mathcards, "\nLeaving MC_NextQuestion()\n");
-
- return 0;
- }
-
- /* 'draw' - copy over the first question */
- MC_CopyCard(&question_list->card, fc);
-
- /* take first question node out of list and move it into active_quests list: */
- ptr = question_list;
- question_list = remove_node(question_list, ptr);
-// free_node(ptr);
- quest_list_length--;
- questions_pending++;
- active_quests = append_node(active_quests, ptr);
-
- if (debug_status & debug_mathcards) {
- printf("\nnext question is:");
- print_card(*fc);
- print_counters();
- printf("\n\nLeaving MC_NextQuestion()\n");
- }
-
- return 1;
-}
-
-
-
-/* MC_AnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has been answered */
-/* correctly. Returns the number of points earned. */
-int MC_AnsweredCorrectly(int id, float t)
-{
- DEBUGMSG(debug_mathcards, "\nEntering MC_AnsweredCorrectly()");
-
- MC_MathQuestion* quest = NULL;
- int points = 0;
-
- if(!active_quests) // No questions currently "in play" - something is wrong:
- {
- fprintf(stderr, "MC_AnsweredCorrectly() - active_quests empty\n");
- return 0;
- }
-
- DEBUGMSG(debug_mathcards, "\nQuestion id was: %d\n", id);
-
- //First take the question out of the active_quests list
- quest = active_quests;
- // Loop until quest is NULL or we find card with same id:
- while(quest && (id != quest->card.question_id))
- quest = quest->next;
- if(!quest) // Means we didn't find matching card - something is wrong:
- {
- fprintf(stderr, "MC_AnsweredCorrectly() - matching question not found!\n");
- return 0;
- }
-
- /* Calculate how many points the player should receive, based on */
- /* difficulty and time required to answer it: */
- points = calc_score(quest->card.difficulty, t);
-
- DEBUGCODE(debug_mathcards)
- {
- printf("\nQuestion was:");
- print_card(quest->card);
- printf("Player recieves %d points\n", points);
- }
-
-
- //We found a matching question, now we take it out of the
- //"active_quests" list and either put it back into the
- //main question list in a random location, or delete it:
- active_quests = remove_node(active_quests, quest);
- questions_pending--; //the length of the 'active_quests' list
- answered_correctly++;
-
- if (!math_opts->iopts[PLAY_THROUGH_LIST])
- /* reinsert question into question list at random location */
- {
- DEBUGMSG(debug_mathcards, "\nReinserting question into list");
-
- MC_MathQuestion* rand_spot;
- /* put it into list */
- rand_spot = pick_random(quest_list_length, question_list);
- question_list = insert_node(question_list, rand_spot, quest);
- quest_list_length++;
- /* unanswered does not change - was not decremented when */
- /* question allocated! */
- }
- else
- {
- DEBUGMSG(debug_mathcards, "\nNot reinserting question into list");
- free_node(quest);
- /* not recycling questions so fewer questions remain: */
- unanswered--;
- }
-
- DEBUGCODE(debug_mathcards)
- {
- print_counters();
- printf("\nLeaving MC_AnsweredCorrectly()\n");
- }
-
- /* Record the time it took to answer: */
- MC_AddTimeToList(t);
-
- return points;
-}
-
-
-
-
-
-/* MC_NotAnsweredCorrectly() is how the user interface */
-/* tells MathCards that the player failed to answer the */
-/* question correctly. Returns 1 if no errors. */
-/* Note: this gets triggered only if a player's igloo/city */
-/* gets hit by a question, not if they "miss". */
-int MC_NotAnsweredCorrectly(int id)
-{
- DEBUGMSG(debug_mathcards, "\nEntering MC_NotAnsweredCorrectly()");
-
- MC_MathQuestion* quest = NULL;
-
- if(!active_quests) // No questions currently "in play" - something is wrong:
- {
- fprintf(stderr, "MC_NotAnsweredCorrectly() - active_quests empty\n");
- return 0;
- }
-
- DEBUGMSG(debug_mathcards, "\nQuestion id was: %d\n", id);
-
- //First take the question out of the active_quests list
- quest = active_quests;
- // Loop until quest is NULL or we find card with same id:
- while(quest && (id != quest->card.question_id))
- quest = quest->next;
- if(!quest) // Means we didn't find matching card - something is wrong:
- {
- fprintf(stderr, "MC_NotAnsweredCorrectly() - matching question not found!\n");
- return 0;
- }
-
- DEBUGMSG(debug_mathcards, "\nMatching question is:");
- print_card(quest->card);
-
-
- /* if desired, put question back in list so student sees it again */
- if (math_opts->iopts[REPEAT_WRONGS])
- {
- int i;
- MC_MathQuestion* quest_copy;
- MC_MathQuestion* rand_loc;
-
- DEBUGMSG(debug_mathcards, "\nAdding %d copies to question_list:", math_opts->iopts[COPIES_REPEATED_WRONGS]);
-
- DEBUGCODE(debug_mathcards)
- {
- print_counters();
- printf("\nLeaving MC_AnsweredCorrectly()\n");
- }
-
- /* can put in more than one copy (to drive the point home!) */
- for (i = 0; i < math_opts->iopts[COPIES_REPEATED_WRONGS]; i++)
- {
- quest_copy = create_node_copy(quest);
- rand_loc = pick_random(quest_list_length, question_list);
- question_list = insert_node(question_list, rand_loc, quest_copy);
- quest_list_length++;
- }
- /* unanswered stays the same if a single copy recycled or */
- /* increases by 1 for each "extra" copy reinserted: */
- unanswered += (math_opts->iopts[COPIES_REPEATED_WRONGS] - 1);
- }
- else
- {
- DEBUGMSG(debug_mathcards, "\nNot repeating wrong answers\n");
- /* not repeating questions so list gets shorter: */
- unanswered--;
- }
-
- //Take the question out of the active_quests list and add it to
- //the wrong_quests list, unless an identical question is already
- //in the wrong_quests list:
- active_quests = remove_node(active_quests, quest);
- questions_pending--; //the length of the 'active_quests' list
- answered_wrong++;
-
- /* add question to wrong_quests list: */
- if (!already_in_list(wrong_quests, quest)) /* avoid duplicates */
- {
- DEBUGMSG(debug_mathcards, "\nAdding to wrong_quests list");
- wrong_quests = append_node(wrong_quests, quest);
- }
- else /* avoid memory leak */
- {
- free_node(quest);
- }
-
- DEBUGCODE(debug_mathcards)
- {
- print_counters();
- printf("\nLeaving MC_NotAnswered_Correctly()\n");
- }
-
- return 1;
-}
-
-
-
-
-
-
-/* Tells user interface if all questions have been answered correctly! */
-/* Requires that at list contained at least one question to start with */
-/* and that wrongly answered questions have been recycled. */
-int MC_MissionAccomplished(void)
-{
- if (starting_length
- && math_opts->iopts[REPEAT_WRONGS]
- && !unanswered)
- {
- return 1;
- }
- else
- {
- return 0;
- }
-}
-
-
-/* Returns number of questions left (either in list */
-/* or "in play") */
-int MC_TotalQuestionsLeft(void)
-{
- return unanswered;
-}
-
-/* Returns number of questions left in list, NOT */
-/* including questions currently "in play". */
-int MC_ListQuestionsLeft(void)
-{
- return quest_list_length;
-}
-
-
-/* Store the amount of time a given flashcard was */
-/* visible on the screen. Returns 1 if the request */
-/* succeeds, 0 otherwise. */
-int MC_AddTimeToList(float t)
-{
- int newsize = 0;
- float *newlist;
-
- //Bail if time invalid:
- if(t < 0)
- return 0;
-
- /* This list will be allocated in an STL-like manner: when the */
- /* list gets full, allocate an additional amount of storage equal */
- /* to the current size of the list, so that only O(logN) allocations */
- /* will ever be needed. We therefore have to keep track of 2 sizes: */
- /* the allocated size, and the actual number of items currently on */
- /* the list. */
- if (length_time_per_question_list >= length_alloc_time_per_question_list) {
- /* The list is full, allocate more space */
- newsize = 2*length_time_per_question_list;
- if (newsize == 0)
- newsize = 100;
- newlist = realloc(time_per_question_list, newsize*sizeof(float));
- if (newlist == NULL)
- {
- DEBUGMSG(debug_mathcards,"\nError: allocation for time_per_question_list failed\n");
- return 0;
- }
- time_per_question_list = newlist;
- length_alloc_time_per_question_list = newsize;
- }
-
- /* Append the time to the list */
- time_per_question_list[length_time_per_question_list++] = t;
- return 1;
-}
-
-/* Frees heap memory used in program: */
-void MC_EndGame(void)
-{
- delete_list(question_list);
- question_list = 0;
- delete_list(wrong_quests);
- wrong_quests = 0;
-
- if (math_opts)
- {
- free(math_opts);
- math_opts = 0;
- }
-
- free(time_per_question_list);
- time_per_question_list = NULL;
- length_alloc_time_per_question_list = 0;
- length_time_per_question_list = 0;
-
- initialized = 0;
-}
-
-
-
-/* prints struct to file */
-void MC_PrintMathOptions(FILE* fp, int verbose)
-{
- int i, vcommentsprimed = 0;
- //comments when writing out verbose...perhaps they can go somewhere less conspicuous
- static char* vcomments[NOPTS];
- if (!vcommentsprimed) //we only want to initialize these once
- {
- vcommentsprimed = 1;
- for (i = 0; i < NOPTS; ++i)
- vcomments[i] = NULL;
- vcomments[PLAY_THROUGH_LIST] =
- "\n############################################################\n"
- "# #\n"
- "# General Math Options #\n"
- "# #\n"
- "# If 'play_through_list' is true, Tuxmath will ask each #\n"
- "# question in an internally-generated list. The list is #\n"
- "# generated based on the question ranges selected below. #\n"
- "# The game ends when no questions remain. #\n"
- "# If 'play_through_list' is false, the game continues #\n"
- "# until all cities are destroyed. #\n"
- "# Default is 1 (i.e. 'true' or 'yes'). #\n"
- "# #\n"
- "# 'question_copies' is the number of times each question #\n"
- "# will be asked. It can be 1 to 10 - Default is 1. #\n"
- "# #\n"
- "# 'repeat_wrongs' tells Tuxmath whether to reinsert #\n"
- "# incorrectly answered questions into the list to be #\n"
- "# asked again. Default is 1 (yes). #\n"
- "# #\n"
- "# 'copies_repeated_wrongs' gives the number of times an #\n"
- "# incorrectly answered question will reappear. Default #\n"
- "# is 1. #\n"
- "# #\n"
- "# The defaults for these values result in a 'mission' #\n"
- "# for Tux that is accomplished by answering all #\n"
- "# questions correctly with at least one surviving city. #\n"
- "############################################################\n\n";
-
- vcomments[FORMAT_ADD_ANSWER_LAST] =
- "\n############################################################\n"
- "# The 'format_<op>_answer_<place> options control #\n"
- "# generation of questions with the answer in different #\n"
- "# places in the equation. i.e.: #\n"
- "# #\n"
- "# format_add_answer_last: 2 + 2 = ? #\n"
- "# format_add_answer_first: ? + 2 = 4 #\n"
- "# format_add_answer_middle: 2 + ? = 4 #\n"
- "# #\n"
- "# By default, 'format_answer_first' is enabled and the #\n"
- "# other two formats are disabled. Note that the options #\n"
- "# are not mutually exclusive - the question list may #\n"
- "# contain questions with different formats. #\n"
- "# #\n"
- "# The formats are set independently for each of the four #\n"
- "# math operations. #\n"
- "############################################################\n\n";
-
- vcomments[ALLOW_NEGATIVES] =
- "\n############################################################\n"
- "# 'allow_negatives' allows or disallows use of negative #\n"
- "# numbers as both operands and answers. Default is 0 #\n"
- "# (no), which disallows questions like: #\n"
- "# 2 - 4 = ? #\n"
- "# Note: this option must be enabled in order to set the #\n"
- "# operand ranges to include negatives (see below). If it #\n"
- "# is changed from 1 (yes) to 0 (no), any negative #\n"
- "# operand limits will be reset to 0. #\n"
- "############################################################\n\n";
-
- vcomments[MAX_ANSWER] =
- "\n############################################################\n"
- "# 'max_answer' is the largest absolute value allowed in #\n"
- "# any value in a question (not only the answer). Default #\n"
- "# is 144. It can be set as high as 999. #\n"
- "############################################################\n\n";
-
- vcomments[MAX_QUESTIONS] =
- "\n############################################################\n"
- "# 'max_questions' is limit of the length of the question #\n"
- "# list. Default is 5000 - only severe taskmasters will #\n"
- "# need to raise it. #\n"
- "############################################################\n\n";
-
- vcomments[RANDOMIZE] =
- "\n############################################################\n"
- "# If 'randomize' selected, the list will be shuffled #\n"
- "# at the start of the game. Default is 1 (yes). #\n"
- "############################################################\n\n";
-
- vcomments[ADDITION_ALLOWED] =
- "\n############################################################\n"
- "# #\n"
- "# Math Operations Allowed #\n"
- "# #\n"
- "# These options enable questions for each of the four math #\n"
- "# operations. All are 1 (yes) by default. #\n"
- "############################################################\n\n";
-
- vcomments[MIN_AUGEND] =
- "\n############################################################\n"
- "# #\n"
- "# Minimum and Maximum Values for Operand Ranges #\n"
- "# #\n"
- "# Operand limits can be set to any integer up to the #\n"
- "# value of 'max_answer'. If 'allow_negatives' is set to 1 #\n"
- "# (yes), either negative or positive values can be used. #\n"
- "# Tuxmath will generate questions for every value in the #\n"
- "# specified range. The maximum must be greater than or #\n"
- "# equal to the corresponding minimum for any questions to #\n"
- "# be generated for that operation. #\n"
- "############################################################\n\n";
-
- }
-
-
- DEBUGMSG(debug_mathcards, "\nEntering MC_PrintMathOptions()\n");
-
- /* bail out if no struct */
- if (!math_opts)
- {
- fprintf(stderr, "\nMath Options struct does not exist!\n");
- return;
- }
-
- for (i = 0; i < NOPTS; ++i)
- {
- if (verbose && vcomments[i] != NULL)
- fprintf(fp, "%s", vcomments[i]);
- fprintf(fp, "%s = %d\n", MC_OPTION_TEXT[i], math_opts->iopts[i]);
- }
- DEBUGMSG(debug_mathcards, "\nLeaving MC_PrintMathOptions()\n");
-}
-
-
-
-int MC_PrintQuestionList(FILE* fp)
-{
- if (fp && question_list)
- {
- print_list(fp, question_list);
- return 1;
- }
- else
- {
- fprintf(stderr, "\nFile pointer and/or question list invalid\n");
- return 0;
- }
-}
-
-int MC_PrintWrongList(FILE* fp)
-{
- if (!fp)
- {
- fprintf(stderr, "File pointer invalid\n");
- return 0;
- }
-
- if (wrong_quests)
- {
- print_list(fp, wrong_quests);
- }
- else
- {
- fprintf(fp, "\nNo wrong questions!\n");
- }
-
- return 1;
-}
-
-
-int MC_StartingListLength(void)
-{
- return starting_length;
-}
-
-
-int MC_WrongListLength(void)
-{
- return list_length(wrong_quests);
-}
-
-int MC_NumAnsweredCorrectly(void)
-{
- return answered_correctly;
-}
-
-
-int MC_NumNotAnsweredCorrectly(void)
-{
- return answered_wrong;
-}
-
-
-/* Report the median time per question */
-float MC_MedianTimePerQuestion(void)
-{
- if (length_time_per_question_list == 0)
- return 0;
-
- qsort(time_per_question_list,length_time_per_question_list,sizeof(float),floatCompare);
- return time_per_question_list[length_time_per_question_list/2];
-}
-
-
-
-
-/* Implementation of "private methods" - (cannot be called from outside
-of this file) */
-
-
-
-/* Resets negative values to zero - used when allow_negatives deselected. */
-void clear_negatives(void)
-{
- int i;
- for (i = MIN_AUGEND; i <= MAX_TYPING_NUM; ++i)
- if (math_opts->iopts[i]< 0)
- math_opts->iopts[i]= 0;
-}
-
-// /* this is used by generate_list to see if a possible question */
-// /* meets criteria to be added to the list or not: */
-// int validate_question(int n1, int n2, int n3)
-// {
-// /* make sure none of values exceeds max_answer using absolute */
-// /* value comparison: */
-// if (abs_value(n1) > abs_value(math_opts->iopts[MAX_ANSWER])
-// || abs_value(n2) > abs_value(math_opts->iopts[MAX_ANSWER])
-// || abs_value(n3) > abs_value(math_opts->iopts[MAX_ANSWER]))
-// {
-// return 0;
-// }
-// /* make sure none of values are negative if negatives not allowed: */
-// if (!math_opts->iopts[ALLOW_NEGATIVES])
-// {
-// if (n1 < 0 || n2 < 0 || n3 < 0)
-// {
-// return 0;
-// }
-// }
-// return 1;
-// }
-
-
-
-// MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard)
-// {
-// MC_MathQuestion* ret = allocate_node();
-// MC_CopyCard(flashcard, &(ret->card));
-// return ret;
-// }
-
-// /* FIXME take care of strings */
-// /* this one copies the contents, including pointers; both nodes must be allocated */
-// int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy)
-// {
-// if (!original)
-// {
-// fprintf(stderr, "\nIn copy_node(): invalid 'original' pointer arg.\n");
-// return 0;
-// }
-// if (!copy)
-// {
-// fprintf(stderr, "\nIn copy_node(): invalid 'copy' pointer arg.\n");
-// return 0;
-// }
-//
-// copy_card(&(original->card), &(copy->card) );
-//
-// copy->next = original->next;
-// copy->previous = original->previous;
-// copy->randomizer = original->randomizer;
-// return 1;
-// }
-
-
-
-
-/* this puts the node into the list AFTER the node pointed to by current */
-/* and returns a pointer to the top of the modified list */
-MC_MathQuestion* insert_node(MC_MathQuestion* first,
- MC_MathQuestion* current,
- MC_MathQuestion* new_node)
-{
- /* return pointer to list unchanged if new_node doesn't exist*/
- if (!new_node)
- return first;
- /* if current doesn't exist, new_node is first */
- if (!current)
- {
- new_node->previous = 0;
- new_node->next =0;
- first = new_node;
- return first;
- }
-
- if (current->next) /* avoid error if at end of list */
- current->next->previous = new_node;
- new_node->next = current->next;
- current->next = new_node;
- new_node->previous = current;
- return first;
-}
-
-
-
-/* adds the new node to the end of the list */
-MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node)
-{
- MC_MathQuestion* ptr;
- /* return pointer to list unchanged if new_node doesn't exist*/
- if (!new_node)
- {
- return list;
- }
-
- /* if list does not exist, new_node is the first (and only) node */
- if (!list)
- {
- return new_node;
- }
- /* otherwise, go to end of list */
- ptr = list;
- while (ptr->next)
- {
- ptr = ptr->next;
- }
-
- ptr->next = new_node;
- new_node->previous = ptr;
- new_node->next = 0;
- return list;
-}
-
-
-
-/* this takes the node out of the list but does not delete it */
-/* and returns a pointer to the top of the modified list */
-MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n)
-{
- if (!n || !first)
- return first;
- /* special case if first node being removed */
- if (n == first)
- first = first->next;
-
- if (n->previous)
- n->previous->next = n->next;
- if (n->next)
- n->next->previous = n->previous;
- n->previous = 0;
- n->next = 0;
- return first;
-}
-
-
-
-/* frees memory for entire list and returns null pointer */
-MC_MathQuestion* delete_list(MC_MathQuestion* list)
-{
- MC_MathQuestion* tmp_ptr;
- while (list)
- {
- tmp_ptr = list->next;
- free_node (list);
- list = tmp_ptr;
- }
- return list;
-}
-
-
-
-void print_list(FILE* fp, MC_MathQuestion* list)
-{
- if (!list)
- {
- fprintf(fp, "\nprint_list(): list empty or pointer invalid\n");
- return;
- }
-
- MC_MathQuestion* ptr = list;
- while (ptr)
- {
- fprintf(fp, "%s\n", ptr->card.formula_string);
- ptr = ptr->next;
- }
-}
-
-
-void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length)
-{
- if (!vect)
- {
- fprintf(fp, "\nprint_vect_list(): list empty or pointer invalid\n");
- return;
- }
-
- int i = 0;
- DEBUGMSG(debug_mathcards, "Entering print_vect_list()\n");
- for(i = 0; i < length; i++)
- fprintf(fp, "%s\n", vect[i]->card.formula_string);
-
- DEBUGMSG(debug_mathcards, "Leaving print_vect_list()\n");
-}
-
-
-
-void print_card(MC_FlashCard card)
-{
- printf("\nprint_card():\n");
- printf("question_id: %d\nformula_string: %s\nanswer_string: %s\n"
- "answer: %d\ndifficulty: %d\n\n",
- card.question_id,
- card.formula_string,
- card.answer_string,
- card.answer,
- card.difficulty);
-}
-
-/* This sends the values of all "global" counters and the */
-/* lengths of the question lists to stdout - for debugging */
-void print_counters(void)
-{
- printf("\nquest_list_length = \t%d", quest_list_length);
- printf("\nlist_length(question_list) = \t%d", list_length(question_list));
- printf("\nstarting_length = \t%d", starting_length);
- printf("\nunanswered = \t%d", unanswered);
- printf("\nanswered_correctly = \t%d", answered_correctly);
- printf("\nanswered_wrong = \t%d", answered_wrong);
- printf("\nlist_length(wrong_quests) = \t%d", list_length(wrong_quests));
- printf("\nquestions_pending = \t%d", questions_pending);
- printf("\nlist_length(active_quests) = \t%d", list_length(active_quests));
-}
-
-
-
-
-
-
-//
-// /* FIXME take care of strings */
-//
-// MC_FlashCard create_card_from_node(MC_MathQuestion* node)
-// {
-// MC_FlashCard fc;
-// if (!node)
-// return DEFAULT_CARD;
-// fc = MC_AllocateFlashcard();
-// copy_card(&(node->card), &fc);
-// return fc;
-// }
-
-
-
-/* a "copy constructor", so to speak */
-/* FIXME perhaps should return newly allocated list if more than one node DSB */
-MC_MathQuestion* create_node_copy(MC_MathQuestion* other)
-{
- MC_MathQuestion* ret = allocate_node();
- if (ret)
- MC_CopyCard(&(other->card), &(ret->card) );
- return ret;
-}
-
-
-int list_length(MC_MathQuestion* list)
-{
- int length = 0;
- while (list)
- {
- length++;
- list = list->next;
- }
- return length;
-}
-
-
-
-
-
-
-/* This is a new implementation written in an attempt to avoid */
-/* the O(n^2) performance problems seen with the old randomization */
-/* function. The list is created as a vector, but is for now still */
-/* made a linked list to minimize changes needed elsewhere. */
-/* The argument is a pointer to the top of the old list. This extra */
-/* level of indirection allows the list to be shuffled "in-place". */
-/* The function returns 1 if successful, 0 on errors. */
-
-static int randomize_list(MC_MathQuestion** old_list)
-{
- MC_MathQuestion* old_tmp = *old_list;
- MC_MathQuestion** tmp_vect = NULL;
-
- int i = 0;
- if (!old_list || !*old_list) //invalid/empty list
- return 0;
-
- int old_length = list_length(old_tmp);
-
- /* set random seed: */
- srand(time(0));
-
-
- /* Allocate vector and set ptrs to nodes in old list: */
-
- /* Allocate a list of pointers, not space for the nodes themselves: */
- tmp_vect = (MC_MathQuestion**)malloc(sizeof(MC_MathQuestion*) * old_length);
- /* Set each pointer in the vector to the corresponding node: */
- for (i = 0; i < old_length; i++)
- {
- tmp_vect[i] = old_tmp;
- tmp_vect[i]->randomizer = rand();
- old_tmp = old_tmp->next;
- }
-
- /* Now simply sort on 'tmp_vect[i]->randomizer' to shuffle list: */
- qsort(tmp_vect, old_length,
- sizeof(MC_MathQuestion*),
- comp_randomizer);
-
- /* Re-create pointers to provide linked-list functionality: */
- /* (stop at 'old_length-1' because we dereference tmp_vect[i+1]) */
- for(i = 0; i < old_length - 1; i++)
- {
- if (!tmp_vect[i])
- {
- fprintf(stderr, "Invalid pointer!\n");
- return 0;
- }
- tmp_vect[i]->next = tmp_vect[i+1];
- tmp_vect[i+1]->previous = tmp_vect[i];
- }
- /* Handle end cases: */
- tmp_vect[0]->previous = NULL;
- tmp_vect[old_length-1]->next = NULL;
-
- /* Now arrange for arg pointer to indirectly point to first element! */
- *old_list = tmp_vect[0];
- free(tmp_vect);
- return 1;
-}
-
-
-
-/* This is needed for qsort(): */
-int comp_randomizer (const void* a, const void* b)
-{
-
- int int1 = (*(const struct MC_MathQuestion **) a)->randomizer;
- int int2 = (*(const struct MC_MathQuestion **) b)->randomizer;
-
- if (int1 > int2)
- return 1;
- else if (int1 == int2)
- return 0;
- else
- return -1;
-}
-
-MC_MathQuestion* pick_random(int length, MC_MathQuestion* list)
-{
- int i;
- int rand_node;
-
- /* set random seed DSB */
- srand(time(0));
-
- /* if length is zero, get out to avoid divide-by-zero error */
- if (0 == length)
- {
- return list;
- }
-
- rand_node = rand() % length;
-
- for (i=1; i < rand_node; i++)
- {
- if (list)
- list = list->next;
- }
-
- return list;
-}
-
-/* compares fields other than pointers */
-int compare_node(MC_MathQuestion* first, MC_MathQuestion* other)
-{
- if (!first || !other)
- return 0;
- if (compare_card(&(first->card), &(first->card) ) ) //cards are equal
- return 1;
- else
- return 0;
-}
-
-/* check to see if list already contains an identical node */
-int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr)
-{
- if (!list || !ptr)
- return 0;
-
- while (list)
- {
- if (compare_node(list, ptr))
- return 1;
- list = list->next;
- }
- return 0;
-}
-
-// /* to prevent option settings in math_opts from getting set to */
-// /* values other than 0 or 1 */
-// int int_to_bool(int i)
-// {
-// if (i)
-// return 1;
-// else
-// return 0;
-// }
-
-// /* prevent values from getting into math_opts that are outside */
-// /* the range that can be handled by the program (i.e. more */
-// /* than three digits; also disallow negatives if that has been */
-// /* selected. */
-// int sane_value(int i)
-// {
-// if (i > MC_GLOBAL_MAX)
-// i = MC_GLOBAL_MAX;
-// else if (i < -MC_GLOBAL_MAX)
-// i = -MC_GLOBAL_MAX;
-//
-// if (i < 0
-// && math_opts
-// && !math_opts->iopts[ALLOW_NEGATIVES])
-// {
-// i = 0;
-// }
-//
-// return i;
-// }
-
-// int abs_value(int i)
-// {
-// if (i > 0)
-// return i;
-// else
-// return -i;
-// }
-
-
-/* Compares two floats (needed for sorting in MC_MedianTimePerQuestion) */
-int floatCompare(const void *v1,const void *v2)
-{
- float f1,f2;
-
- f1 = *((float *) v1);
- f2 = *((float *) v2);
-
- if (f1 < f2)
- return -1;
- else if (f1 > f2)
- return 1;
- else
- return 0;
-}
-
-
-
-/****************************************************
-Functions for new mathcards architecture
-****************************************************/
-
-void MC_CopyCard(const MC_FlashCard* src, MC_FlashCard* dest)
-{
- if (!src || !dest)
- return;
- DEBUGMSG(debug_mathcards, "Copying '%s' to '%s', ", src->formula_string,dest->formula_string);
- DEBUGMSG(debug_mathcards, "copying '%s' to '%s'\n", src->answer_string, dest->answer_string);
- strncpy(dest->formula_string, src->formula_string, MC_FORMULA_LEN);
- strncpy(dest->answer_string, src->answer_string, MC_ANSWER_LEN);
- DEBUGMSG(debug_mathcards, "Card is: '%s', '%s'\n", dest->formula_string, dest->answer_string);
- dest->answer = src->answer;
- dest->difficulty = src->difficulty;
- dest->question_id = src->question_id;
-}
-
-void free_node(MC_MathQuestion* mq) //no, not that freenode.
-{
- if (!mq)
- return;
- MC_FreeFlashcard(&(mq->card) );
- free(mq);
-}
-
-MC_MathQuestion* allocate_node()
-{
- MC_MathQuestion* ret = NULL;
- ret = malloc(sizeof(MC_MathQuestion) );
- if (!ret)
- {
- printf("Could not allocate space for a new node!\n");
- return NULL;
- }
-
- ret->card = MC_AllocateFlashcard();
- ret->next = ret->previous = NULL;
-
- return ret;
-}
-
-/*
-The function that does the central dirty work pertaining to flashcard
-creation. Extensible to just about any kind of math problem, perhaps
-with the exception of those with multiple answers, such as "8 + 2 > ?"
-Simply specify how the problem is presented to the user, and the
-answer the game should look for, as strings.
-*/
-MC_FlashCard generate_random_flashcard(void)
-{
- int num;
- int length;
- MC_ProblemType pt;
- MC_FlashCard ret;
- static int generate_random_flashcard_id=0;
-
- generate_random_flashcard_id+=1;
- DEBUGMSG(debug_mathcards, "Entering generate_random_flashcard()\n");
- DEBUGMSG(debug_mathcards, "ID is %d\n",generate_random_flashcard_id);
-
- //choose a problem type
- do
- pt = rand() % MC_NUM_PTYPES;
- while ( (pt == MC_PT_TYPING && !MC_GetOpt(TYPING_PRACTICE_ALLOWED) ) ||
- (pt == MC_PT_ARITHMETIC && !MC_GetOpt(ADDITION_ALLOWED) &&
- !MC_GetOpt(SUBTRACTION_ALLOWED) &&
- !MC_GetOpt(MULTIPLICATION_ALLOWED) &&
- !MC_GetOpt(DIVISION_ALLOWED) ) ||
- (pt == MC_PT_COMPARISON && !MC_GetOpt(COMPARISON_ALLOWED) )
- );
-
- if (pt == MC_PT_TYPING) //typing practice
- {
- DEBUGMSG(debug_mathcards, "Generating typing question\n");
- ret = MC_AllocateFlashcard();
- num = rand() % (MC_GetOpt(MAX_TYPING_NUM)-MC_GetOpt(MIN_TYPING_NUM) + 1)
- + MC_GetOpt(MIN_TYPING_NUM);
- snprintf(ret.formula_string, MC_FORMULA_LEN, "%d", num);
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", num);
- ret.answer = num;
- ret.difficulty = 10;
- ret.question_id=generate_random_flashcard_id;
- }
- else //if (pt == MC_PT_ARITHMETIC)
- {
- DEBUGMSG(debug_mathcards, "Generating arithmetic question");
- length = rand() % (MC_GetOpt(MAX_FORMULA_NUMS) -
- MC_GetOpt(MIN_FORMULA_NUMS) + 1) //avoid div by 0
- + MC_GetOpt(MIN_FORMULA_NUMS);
- DEBUGMSG(debug_mathcards, " of length %d", length);
- ret = generate_random_ooo_card_of_length(length, 1);
-
- if (debug_status & debug_mathcards) {
- print_card(ret);
- }
- }
- //TODO comparison problems (e.g. "6 ? 9", "<")
-
- DEBUGMSG(debug_mathcards, "Exiting generate_random_flashcard()\n");
-
- return ret;
-}
-
-/*
-Recursively generate an order of operations problem. Hopefully this won't
-raise performance issues. Difficulty is calculated based on the length of
-the formula and on the operators used. Problems have a 'base' difficulty of
-1 for binary operations, 3 for 3 numbers, 6, 10, etc. Each operator adds to
-the score: 0, 1, 2, and 3 respectively for addition, subtraction,
-multiplication and division.If reformat is 0, FORMAT_ANS_LAST will be used,
-otherwise a format is chosen at random.
-*/
-
-/* FIXME we should consider rewriting this - it currently generates some
-questions with indeterminate answers (e.g. ? * 0 = 0) that seem to be
-impossible to prevent using the current scheme with recursive string
-operations.
-*/
-
-MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat)
-{
- int format = 0;
- int r1 = 0;
- int r2 = 0;
- int ans = 0;
- char tempstr[MC_FORMULA_LEN];
- MC_FlashCard ret;
- MC_Operation op;
-
- id += 1;
- DEBUGMSG(debug_mathcards, ".");
- if (length > MAX_FORMULA_NUMS)
- return DEFAULT_CARD;
- if (length <= 2)
- {
- DEBUGMSG(debug_mathcards, "\n");
- ret = MC_AllocateFlashcard();
- for (op = rand() % MC_NUM_OPERS; //pick a random operation
- MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
- op = rand() % MC_NUM_OPERS);
-
- DEBUGMSG(debug_mathcards, "Operation is %c\n", operchars[op]);
- /*
- if (op == MC_OPER_ADD)
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
- r2 = rand() % (math_opts->iopts[MAX_ADDEND] - math_opts->iopts[MIN_ADDEND] + 1) + math_opts->iopts[MIN_ADDEND];
- ans = r1 + r2;
- }
- else if (op == MC_OPER_SUB)
- {
- r1 = rand() % (math_opts->iopts[MAX_MINUEND] - math_opts->iopts[MIN_MINUEND] + 1) + math_opts->iopts[MIN_MINUEND];
- r2 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
- ans = r1 - r2;
- }
- else if (op == MC_OPER_MULT)
- {
- r1 = rand() % (math_opts->iopts[MAX_MULTIPLIER] - math_opts->iopts[MIN_MULTIPLIER] + 1) + math_opts->iopts[MIN_MULTIPLIER];
- r2 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_MULTIPLICAND];
- ans = r1 * r2;
- }
- else if (op == MC_OPER_DIV)
- {
- ans = rand() % (math_opts->iopts[MAX_QUOTIENT] - math_opts->iopts[MIN_QUOTIENT] + 1) + math_opts->iopts[MIN_QUOTIENT];
- r2 = rand() % (math_opts->iopts[MAX_DIVISOR] - math_opts->iopts[MIN_DIVISOR] + 1) + math_opts->iopts[MIN_DIVISOR];
- if (r2 == 0)
- r2 = 1;
- r1 = ans * r2;
- }
- */
- if (op > MC_OPER_DIV || op < MC_OPER_ADD)
- {
- DEBUGMSG(debug_mathcards, "Invalid operator: value %d\n", op);
- return DEFAULT_CARD;
- }
- //choose two numbers in the proper range and get their result
-
- else do
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND+4*op] - math_opts->iopts[MIN_AUGEND+4*op] + 1) + math_opts->iopts[MIN_AUGEND+4*op];
- r2 = rand() % (math_opts->iopts[MAX_ADDEND+4*op] - math_opts->iopts[MIN_ADDEND+4*op] + 1) + math_opts->iopts[MIN_ADDEND+4*op];
-
- if (op == MC_OPER_ADD)
- ans = r1 + r2;
- if (op == MC_OPER_SUB)
- ans = r1 - r2;
- if (op == MC_OPER_MULT)
- ans = r1 * r2;
- if (op == MC_OPER_DIV)
- {
- if (r2 == 0)
- r2 = 1;
- ret.difficulty = r1;
- r1 *= r2;
- ans = ret.difficulty;
- }
- } while ( (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES)) || ans > MC_GetOpt(MAX_ANSWER) );
-
-
- DEBUGMSG(debug_mathcards, "Constructing answer_string\n");
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", ans);
- DEBUGMSG(debug_mathcards, "Constructing formula_string\n");
- snprintf(ret.formula_string, MC_FORMULA_LEN, "%d %c %d",
- r1, operchars[op], r2);
- ret.answer = ans;
- DEBUGMSG(debug_mathcards, "int answer is %d\n", ret.answer);
- ret.difficulty = op + 1;
-
- }
- else //recurse
- {
- ret = generate_random_ooo_card_of_length(length - 1, 0);
-
- if (strchr(ret.formula_string, '+') || strchr(ret.formula_string, '-') )
- {
- //if the expression has addition or subtraction, we can't assume that
- //introducing multiplication or division will produce a predictable
- //result, so we'll limit ourselves to more addition/subtraction
- for (op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB;
- MC_GetOpt(op + ADDITION_ALLOWED) == 0;
- op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB);
-
- }
- else
- {
- //the existing expression can be treated as a number in itself, so we
- //can do anything to it and be confident of the result.
- for (op = rand() % MC_NUM_OPERS; //pick a random operation
- MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
- op = rand() % MC_NUM_OPERS);
- }
- DEBUGMSG(debug_mathcards, "Next operation is %c,", operchars[op]);
-
- //pick the next operand
- if (op == MC_OPER_ADD)
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
- ret.answer += r1;
- }
- else if (op == MC_OPER_SUB)
- {
- r1 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
- ret.answer -= r1;
- }
- else if (op == MC_OPER_MULT)
- {
- r1 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_AUGEND];
- ret.answer *= r1;
- }
- else if (op == MC_OPER_DIV)
- {
- r1 = find_divisor(ret.answer);
- ret.answer /= r1;
- }
- else
- {
- ; //invalid operator
- }
- DEBUGMSG(debug_mathcards, " operand is %d\n", r1);
- DEBUGMSG(debug_mathcards, "Answer: %d\n", ret.answer);
-
- //next append or prepend the new number (might need optimization)
- if (op == MC_OPER_SUB || op == MC_OPER_DIV || //noncommutative, append only
- rand() % 2)
- {
- snprintf(tempstr, MC_FORMULA_LEN, "%s %c %d", //append
- ret.formula_string, operchars[op], r1);
- strncpy(ret.formula_string, tempstr, MC_FORMULA_LEN);
- }
- else //we're prepending
- {
- snprintf(tempstr, MC_FORMULA_LEN, "%d %c %s", //append
- r1, operchars[op], ret.formula_string);
- strncpy(ret.formula_string, tempstr, MC_FORMULA_LEN);
- }
-
- //finally update the answer and score
- snprintf(ret.answer_string, MC_ANSWER_LEN, "%d", ret.answer);
- ret.difficulty += (length - 1) + op;
- }
-
- if (reformat)
- {
- DEBUGMSG(debug_mathcards, "Reformatting...\n");
- do {
- format = rand() % MC_NUM_FORMATS;
- } while (!MC_GetOpt(FORMAT_ANSWER_LAST + format) &&
- !MC_GetOpt(FORMAT_ADD_ANSWER_LAST + op * 3 + format) );
-
- strncat(ret.formula_string, " = ?", MC_FORMULA_LEN - strlen(ret.formula_string) );
- DEBUGMSG(debug_mathcards, "Formula_string: %s\n", ret.formula_string);
- reformat_arithmetic(&ret, format );
- }
- ret.question_id = id;
-
- DEBUGMSG(debug_mathcards, "At end of generate_rand_ooo_card_of_length():\n");
- print_card(ret);
-
- return ret;
-}
-
-
-
-MC_MathQuestion* generate_list(void)
-{
- int i, j;
- int length = MC_GetOpt(AVG_LIST_LENGTH);
- int cl; //raw length
- double r1, r2, delta, var; //randomizers for list length
- MC_MathQuestion* list = NULL;
- MC_MathQuestion* end_of_list = NULL;
- MC_MathQuestion* tnode = NULL;
-
- if (debug_status & debug_mathcards)
- MC_PrintMathOptions(stdout, 0);
-
- if (!(MC_GetOpt(ARITHMETIC_ALLOWED) ||
- MC_GetOpt(TYPING_PRACTICE_ALLOWED) ||
- MC_GetOpt(COMPARISON_ALLOWED) ) )
- return NULL;
-
- //randomize list length by a "bell curve" centered on average
- if (length && MC_GetOpt(VARY_LIST_LENGTH) )
- {
- r1 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
- r2 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
- DEBUGMSG(debug_mathcards, "Randoms chosen: %5f, %5f\n", r1, r2);
- delta = sqrt(-2 * log(r1) ) * cos(2 * PI_VAL * r2); //standard normal dist.
- var = length / 10.0; //variance
- delta = delta * var;
- DEBUGMSG(debug_mathcards, "Delta of average is %5f\n", delta);
- length += delta;
- if (length < 0)
- length = 1; //just in case...
- }
-
- if (MC_GetOpt(COMPREHENSIVE)) //generate all
- {
- int num_valid_questions; //How many questions the COMPREHENSIVE list specifies
- int cycles_needed; //How many times we need to generate it to get enough
-
- num_valid_questions = calc_num_valid_questions();
- if(num_valid_questions == 0)
- {
- fprintf(stderr, "generate_list() - no valid questions\n");
- return NULL;
- }
-
- cycles_needed = length/num_valid_questions;
-
- if((cycles_needed * num_valid_questions) < length)
- cycles_needed++;
-
- DEBUGMSG(debug_mathcards, "In generate_list() - COMPREHENSIVE method requested\n");
- DEBUGMSG(debug_mathcards, "num_valid_questions = %d\t cycles_needed = %d\n",
- num_valid_questions, cycles_needed);
-
- for (i = MC_PT_TYPING; i < MC_NUM_PTYPES; ++i)
- {
- if (!MC_GetOpt(i + TYPING_PRACTICE_ALLOWED))
- continue;
- for (j = 0; j < cycles_needed; j++)
- list = add_all_valid(i, list, &end_of_list);
- }
-
-
- if (MC_GetOpt(RANDOMIZE) )
- {
- DEBUGMSG(debug_mathcards, "Randomizing list\n");
- randomize_list(&list);
- }
-
- if (length)
- {
- cl = list_length(list);
- // NOTE this should no longer happen - we run the COMPREHENSIVE
- // generation until we have enough questions.
- if (length > cl) //if not enough questions, pad out with randoms
- {
- DEBUGMSG(debug_mathcards, "Padding out list from %d to %d questions\n", cl, length);
- for (i = cl; i < length; ++i)
- {
- tnode = malloc(sizeof(MC_MathQuestion) );
- if(!tnode)
- {
- fprintf(stderr, "In generate_list() - allocation failed!\n");
- delete_list(list);
- return NULL;
- }
-
- tnode->card = generate_random_flashcard();
- list = insert_node(list, end_of_list, tnode);
- end_of_list = tnode;
-// DEBUGMSG(debug_mathcards, "%d.", list_length(list) );
- }
- }
- else if (length < cl) //if too many questions, chop off tail end of list
- {
- DEBUGMSG(debug_mathcards, "Cutting list to %d questions\n", length);
- end_of_list = find_node(list, length);
- delete_list(end_of_list->next);
- end_of_list->next = NULL;
- }
- }
- }
-
- /* Here we are just generating random questions, one at a */
- /* time until we have enough */
- /* NOTE generate_random_flashcard() has some bugs, so only */
- /* use this method if we need multiple operand questions */
- else
- {
- DEBUGMSG(debug_mathcards, "In generate_list() - COMPREHENSIVE method NOT requested\n");
-
- for (i = 0; i < length; ++i)
- {
- tnode = malloc(sizeof(MC_MathQuestion) );
- if(!tnode)
- {
- fprintf(stderr, "In generate_list() - allocation failed!\n");
- delete_list(list);
- return NULL;
- }
-
- tnode->card = generate_random_flashcard();
- list = insert_node(list, end_of_list, tnode);
- end_of_list = tnode;
- }
- }
- /* Now just put the question_id values in: */
-
- {
- int i = 1;
- MC_MathQuestion* ptr = list;
- while(ptr->next)
- {
- ptr->card.question_id = i;
- ptr = ptr->next;
- i++;
- }
- }
-
- return list;
-}
-
-/* NOTE - returns 0 (i.e. "false") if *identical*, and */
-/* 1 (i.e. "true") if *different* - counterintuitive, */
-/* but same behavior as e.g. strcmp() */
-
-static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b)
-{
- if (strncmp(a->formula_string, b->formula_string, MC_FORMULA_LEN) )
- return 1;
- if (strncmp(a->answer_string, b->answer_string, MC_ANSWER_LEN) )
- return 1;
- if (a->answer != b->answer);
- return 1;
-
- return 0; //the cards are identical
-}
-
-/* Public functions */
-
-/* allocate space for an MC_Flashcard */
-MC_FlashCard MC_AllocateFlashcard(void)
-{
- MC_FlashCard ret;
-
-//NOTE strings now simply hard-coded to MC_FORMULA_LEN (= 40) and
-//MC_ANSWER_LEN (= 5) instead of tailoring them to save a few bytes - DSB
-// DEBUGMSG(debug_mathcards, "Allocating %d + %d bytes for flashcard\n",
-// max_formula_size + 1, max_answer_size + 1);
-// ret.formula_string = malloc( (max_formula_size + 1) * sizeof(char));
-// ret.answer_string = malloc( (max_answer_size + 1) * sizeof(char));
-// if (!ret.formula_string || !ret.answer_string)
-// {
-// free(ret.formula_string);
-// free(ret.answer_string);
-// printf("Couldn't allocate space for a new flashcard!\n");
-// ret = DEFAULT_CARD;
-// }
- return ret;
-}
-
-//Now a no-op - MC_FlashCard no longer has dynamically allocated strings
-void MC_FreeFlashcard(MC_FlashCard* fc)
-{
- return;
-// if (!fc)
-// return;
-// // DEBUGMSG(debug_mathcards, "Freeing formula_string\n");
-// if (fc->formula_string)
-// {
-// free(fc->formula_string);
-// fc->formula_string = NULL;
-// }
-// // DEBUGMSG(debug_mathcards, "Freeing answer_string\n");
-// if (fc->answer_string)
-// {
-// free(fc->answer_string);
-// fc->answer_string = NULL;
-// }
-}
-
-unsigned int MC_MapTextToIndex(const char* text)
-{
- int i;
- for (i = 0; i < NOPTS; ++i)
- {
- if (!strcasecmp(text, MC_OPTION_TEXT[i]) )
- return i;
- }
- DEBUGMSG(debug_mathcards, "'%s' isn't a math option\n", text);
- return NOT_VALID_OPTION;
-}
-
-
-void MC_SetOpt(unsigned int index, int val)
-{
- if (index >= NOPTS)
- {
- DEBUGMSG(debug_mathcards, "Invalid math option index: %d\n", index);
- return;
- }
-
- /* Do some sanity checks before we throw val into the struct: */
- switch(index)
- {
- /* All the booleans must be 0 or 1: */
- case PLAY_THROUGH_LIST:
- case REPEAT_WRONGS:
- case ALLOW_NEGATIVES:
- case FORMAT_ANSWER_LAST:
- case FORMAT_ANSWER_FIRST:
- case FORMAT_ANSWER_MIDDLE:
- case FORMAT_ADD_ANSWER_LAST:
- case FORMAT_ADD_ANSWER_FIRST:
- case FORMAT_ADD_ANSWER_MIDDLE:
- case FORMAT_SUB_ANSWER_LAST:
- case FORMAT_SUB_ANSWER_FIRST:
- case FORMAT_SUB_ANSWER_MIDDLE:
- case FORMAT_MULT_ANSWER_LAST:
- case FORMAT_MULT_ANSWER_FIRST:
- case FORMAT_MULT_ANSWER_MIDDLE:
- case FORMAT_DIV_ANSWER_LAST:
- case FORMAT_DIV_ANSWER_FIRST:
- case FORMAT_DIV_ANSWER_MIDDLE:
- case ADDITION_ALLOWED:
- case SUBTRACTION_ALLOWED:
- case MULTIPLICATION_ALLOWED:
- case DIVISION_ALLOWED:
- case TYPING_PRACTICE_ALLOWED:
- case ARITHMETIC_ALLOWED:
- case COMPARISON_ALLOWED:
- case RANDOMIZE:
- case COMPREHENSIVE:
- case VARY_LIST_LENGTH:
- {
- /* Reset all non-zero values to one: */
- if(val)
- {
- if(val != 1)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to 1\n", MC_OPTION_TEXT[index], val);
- val = 1;
- }
- }
- break;
- }
-
- /* Parameters concerning numbers of questions */
- /* must be greater than or equal to zero: */
- /* TODO some additional checks would make sense */
- case QUESTION_COPIES:
- case COPIES_REPEATED_WRONGS:
- case MAX_QUESTIONS:
- case MAX_FORMULA_NUMS:
- case MIN_FORMULA_NUMS:
- case AVG_LIST_LENGTH:
- {
- /* Reset all negative values to zero: */
- if(val < 0)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to 0\n", MC_OPTION_TEXT[index], val);
- val = 0;
- }
- break;
- }
-
- /* Operand values - make sure they are in displayable range */
- /* i.e. -999 to 999 */
- case MAX_ANSWER:
- case MIN_AUGEND:
- case MAX_AUGEND:
- case MIN_ADDEND:
- case MAX_ADDEND:
- case MIN_MINUEND:
- case MAX_MINUEND:
- case MIN_SUBTRAHEND:
- case MAX_SUBTRAHEND:
- case MIN_MULTIPLIER:
- case MAX_MULTIPLIER:
- case MIN_MULTIPLICAND:
- case MAX_MULTIPLICAND:
- case MIN_DIVISOR:
- case MAX_DIVISOR:
- case MIN_QUOTIENT:
- case MAX_QUOTIENT:
- case MIN_TYPING_NUM:
- case MAX_TYPING_NUM:
- case MIN_COMPARATOR:
- case MAX_COMPARATOR:
- case MIN_COMPARISAND:
- case MAX_COMPARISAND:
- {
- if(val > MC_GLOBAL_MAX)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to %d\n", MC_OPTION_TEXT[index],
- val, MC_GLOBAL_MAX);
- val = MC_GLOBAL_MAX;
- }
-
- if(val < (0 - MC_GLOBAL_MAX))
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to %d\n", MC_OPTION_TEXT[index],
- val, (0 - MC_GLOBAL_MAX));
- val = (0 - MC_GLOBAL_MAX);
- }
-
- break;
- }
-
- default:
- fprintf(stderr, "Warning - in MC_SetOpt() - unrecognized index %d\n",
- index);
- }
- /* Should now be safe to put "sanitized" value into struct: */
- math_opts->iopts[index] = val;
-}
-
-int MC_GetOpt(unsigned int index)
-{
- if (index >= NOPTS)
- {
- DEBUGMSG(debug_mathcards, "Invalid option index: %d\n", index);
- return MC_MATH_OPTS_INVALID;
- }
- if (!math_opts)
- {
- printf("Invalid options list!\n");
- return MC_MATH_OPTS_INVALID;
- }
- return math_opts->iopts[index];
-}
-
-int MC_VerifyOptionListSane(void)
-{
- return strcmp(MC_OPTION_TEXT[NOPTS], "END_OF_OPTS") == 0;
-}
-
-int MC_MaxFormulaSize(void)
-{
- return MC_FORMULA_LEN;
-}
-
-int MC_MaxAnswerSize(void)
-{
- return MC_ANSWER_LEN;
-}
-
-void MC_ResetFlashCard(MC_FlashCard* fc)
-{
- if (!fc || !fc->formula_string || !fc->answer_string)
- return;
- fc->formula_string[0] = '\0';
- fc->answer_string[0] = '\0';
- fc->answer = -9999;
- fc->difficulty = 0;
- fc->question_id = -1;
-}
-
-int MC_FlashCardGood(const MC_FlashCard* fc)
-{
- return fc && fc->formula_string && fc->answer_string;
-}
-
-int find_divisor(int a)
-{
- int div = 1; //the divisor to return
- int realisticpasses = 3; //reasonable time after which a minimum should be met
- int i;
- do
- for (i = 0; i < NPRIMES; ++i) //test each prime
- if (a % smallprimes[i] == 0) //if it is a prime factor,
- if (rand() % (i + 1) == 0) //maybe we'll keep it
- if (div * smallprimes[i] <= MC_GetOpt(MAX_DIVISOR) ) //if we can,
- div *= smallprimes[i]; //update our real divisor
- //keep going if the divisor is too small
- while (div < MC_GetOpt(MIN_DIVISOR) && --realisticpasses);
-
- return div;
-}
-
-
-//Computes (approximately) the number of questions that will be returned
-//by add_all_valid() as specified by the current options. This does not
-//take into account screening out of invalid questions, such
-//as divide-by-zero and questions like "0 x ? = 0".
-static int calc_num_valid_questions(void)
-{
- int total_questions = 0;
- int k = 0;
- //First add the number of typing questions
- if (MC_GetOpt(TYPING_PRACTICE_ALLOWED))
- total_questions += (MC_GetOpt(MAX_TYPING_NUM) - MC_GetOpt(MIN_TYPING_NUM));
-
- //Now add how many questions we will have for each operation:
- for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
- {
- int num_this_oper = 0;
- int formats_this_oper = 0;
-
- if (!MC_GetOpt(k + ADDITION_ALLOWED) )
- continue;
-
- //calculate number of ordered pairs of first and second operands:
- //note the "+ 1" is due to the ranges being inclusive
- num_this_oper = (MC_GetOpt(MAX_AUGEND + 4 * k) - MC_GetOpt(MIN_AUGEND + 4 * k) + 1)
- *
- (MC_GetOpt(MAX_ADDEND + 4 * k) - MC_GetOpt(MIN_ADDEND + 4 * k) + 1);
- //check what formats are allowed
- if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
- formats_this_oper++;
- if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3))
- formats_this_oper++;
- if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
- formats_this_oper++;
- //Get total of e.g. addition questions:
- num_this_oper *= formats_this_oper;
- //add to overall total:
- total_questions += num_this_oper;
- }
-
- //TODO will also need to count up the COMPARISON questions once
- //they are implemented
- {
- }
-
- DEBUGMSG(debug_mathcards, "calc_num_valid_questions():\t%d\n", total_questions);
- return total_questions;
-}
-
-
-
-
-//NOTE end_of_list** needs to be doubly indirect because otherwise the end does not
-//get updated in the calling code
-//NOTE the difficulty is set as add = 1, sub = 2, mult = 3, div = 4, plus a 2 point
-//bonus if the format is a "missing number".
-MC_MathQuestion* add_all_valid(MC_ProblemType pt,
- MC_MathQuestion* list,
- MC_MathQuestion** end_of_list)
-{
- int i, j;
- int ans = 0, tmp;
- MC_Operation k;
- MC_MathQuestion* tnode;
-
- DEBUGMSG(debug_mathcards, "Entering add_all_valid(%d)\n", pt);
- DEBUGMSG(debug_mathcards, "List already has %d questions\n", list_length(list));
-
- //make sure this problem type is actually allowed
- if (!MC_GetOpt(pt + TYPING_PRACTICE_ALLOWED) )
- return list;
-
- //add all typing questions in range
- if (pt == MC_PT_TYPING)
- {
- DEBUGMSG(debug_mathcards, "Adding typing...\n");
- for (i = MC_GetOpt(MIN_TYPING_NUM); i <= MC_GetOpt(MAX_TYPING_NUM); ++i)
- {
- DEBUGMSG(debug_mathcards, "(%d)\n", i);
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN, "%d", i);
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", i);
- tnode->card.answer = i;
- tnode->card.difficulty = 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- }
-
- //add all allowed arithmetic questions
- else if (MC_PT_ARITHMETIC)
- {
- DEBUGMSG(debug_mathcards, "Adding arithmetic...\n");
-
- // The k loop iterates through the four arithmetic operations:
- // k = 0 means addition
- // k = 1 means subtraction
- // k = 2 means multiplication
- // k = 3 means division
- for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
- {
- if (!MC_GetOpt(k + ADDITION_ALLOWED) )
- continue;
- DEBUGMSG(debug_mathcards, "\n*%d*\n", k);
-
- // The i loop iterates through the first value in the question:
- for (i = MC_GetOpt(MIN_AUGEND + 4 * k); i <= MC_GetOpt(MAX_AUGEND + 4 * k); ++i)
- {
- DEBUGMSG(debug_mathcards, "\n%d:\n", i);
-
- // The j loop iterates through the second value in the question:
- for (j = MC_GetOpt(MIN_ADDEND + 4 * k); j <= MC_GetOpt(MAX_ADDEND + 4 * k); ++j)
- {
- // Generate the third number according to the operation.
- // Although it is called "ans", it will not be the actual
- // answer if it is a "missing number" type problem
- // (e.g. "3 x ? = 12")
- // We also filter out invalid questions here
- switch (k)
- {
- case MC_OPER_ADD:
- {
- ans = i + j;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_SUB:
- {
- ans = i - j;
- // throw out negatives if they aren't allowed:
- if (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES))
- continue;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_MULT:
- {
- ans = i * j;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_DIV:
- {
- // throw anything over MAX_ANSWER
- if (i * j > MC_GetOpt(MAX_ANSWER))
- continue;
-
- tmp = i;
- i *= j;
- ans = j;
- j = tmp;
- break;
- }
- default:
- fprintf(stderr, "Unrecognized operation type: %d\n", k);
- continue;
- }
-
- DEBUGMSG(debug_mathcards, "Generating: %d %c %d = %d\n", i, operchars[k], j, ans);
-
- //add each format, provided it's allowed in general and for this op
-
- // Questions like "a + b = ?"
- if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
- {
- // Avoid division by zero:
- if (k == MC_OPER_DIV && j == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", ans);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "%d %c %d = ?", i, operchars[k], j);
- tnode->card.difficulty = k + 1;
- tnode->card.answer = ans;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
-
-
- // Questions like "? + b = c"
- if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3) )
- {
- // Avoid questions with indeterminate answer:
- // e.g. "? x 0 = 0"
- if (k == MC_OPER_MULT && j == 0)
- {
- continue;
- }
- // Avoid division by zero:
- if (k == MC_OPER_DIV && j == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", i);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "? %c %d = %d", operchars[k], j, ans);
- tnode->card.answer = ans;
- tnode->card.difficulty = k + 3;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
-
-
- // Questions like "a + ? = c"
- if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
- {
- // Avoid questions with indeterminate answer:
- // e.g. "0 x ? = 0"
- if (k == MC_OPER_MULT && i == 0)
- continue;
-
- // e.g. "0 / ? = 0"
- if (k == MC_OPER_DIV && i == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN, "%d", j);
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN,
- "%d %c ? = %d", i, operchars[k], ans);
- tnode->card.answer = ans;
- tnode->card.difficulty = k + 3;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- //If we divided, reset i and j so loop works correctly
- if (k == MC_OPER_DIV)
- {
- j = ans;
- i = tmp;
- DEBUGMSG(debug_mathcards, "resetting to %d %d\n", j, i);
- }
- }
- }
- }
- }
- //add all comparison questions (TODO implement them!)
- else if (pt == MC_PT_COMPARISON)
- {
- for (i = MC_GetOpt(MIN_COMPARATOR); i < MC_GetOpt(MAX_COMPARATOR); ++i)
- {
- for (j = MC_GetOpt(MIN_COMPARISAND); j < MC_GetOpt(MAX_COMPARISAND); ++j)
- {
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.formula_string, MC_FORMULA_LEN, "%d ? %d", i,j);
- snprintf(tnode->card.answer_string, MC_ANSWER_LEN,
- i < j ? "<" :
- i > j ? ">" :
- "=");
- tnode->card.difficulty = 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- }
- }
- DEBUGMSG(debug_mathcards, "Exiting add_all_valid()\n");
- DEBUGMSG(debug_mathcards, "List now has %d questions\n\n", list_length(list));
-
- return list;
-}
-
-MC_MathQuestion* find_node(MC_MathQuestion* list, int num)
-{
- while (--num > 0 && list)
- list = list->next;
- return list;
-}
-
-void reformat_arithmetic(MC_FlashCard* card, MC_Format f)
-{
- int i, j;
- char* beg = 0;
- char* end = 0;
- char nans[MC_ANSWER_LEN];
- char nformula[MC_FORMULA_LEN + MC_ANSWER_LEN + 1]; //gets a bit larger than usual in the meantime
-
- {
- DEBUGMSG(debug_mathcards, "Starting formula: %s\n", card->formula_string);
- //insert old answer where question mark was
- for (i = 0, j = 0; card->formula_string[j] != '?'; ++i, ++j)
- nformula[i] = card->formula_string[j];
- i += snprintf(nformula + i, MC_ANSWER_LEN, "%s", card->answer_string);
- DEBUGMSG(debug_mathcards, "interim formula: %s\n", nformula);
- snprintf(nformula + i, MC_FORMULA_LEN - i, "%s", card->formula_string + j + 1);
-
- //replace the new answer with a question mark
- if (f == MC_FORMAT_ANS_LAST)
- beg = strrchr(nformula, ' ') + 1;
- if (f == MC_FORMAT_ANS_FIRST)
- beg = nformula;
- if (f == MC_FORMAT_ANS_MIDDLE)
- beg = strchr(nformula, ' ') + 3;
- end = strchr(beg + 1, ' ');
- if (!end)
- end = "";
-
- //we now have beg = first digit of number to replace, end = the char after
- sscanf(beg, "%s", nans);
- *beg = 0; //sequester the first half of the string
- snprintf(card->formula_string, MC_FORMULA_LEN, "%s?%s", nformula, end);
- DEBUGMSG(debug_mathcards, "New formula: %s\n", card->formula_string);
- snprintf(card->answer_string, MC_ANSWER_LEN, "%s", nans);
- card->answer = atoi(card->answer_string);
- }
-}
-
-static int calc_score(int difficulty, float t)
-{
- if (t < 0 || difficulty < 1)
- return 0;
- return (difficulty * SCORE_COEFFICIENT)/t;
-}
diff --git a/src/mathcards.h b/src/mathcards.h
deleted file mode 100644
index 368d1ea..0000000
--- a/src/mathcards.h
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
-
- mathcards.h
-
- Description: contains headers for a flashcard-type math game.
- This is a sort of interface-independent backend that could be used with a different
- user interface. Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
- (aka tuxmath). If tuxmath were a C++ program, this would be a C++ class.
-
- Author: David Bruce <davidstuartbruce at gmail.com>, (C) 2006
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-
-
-#ifndef MATHCARDS_H
-#define MATHCARDS_H
-
-#include "transtruct.h"
-
-
-/* different classes of problems TuxMath will ask */
-typedef enum _MC_ProblemType {
- MC_PT_TYPING,
- MC_PT_ARITHMETIC,
- MC_PT_COMPARISON,
- MC_NUM_PTYPES
-} MC_ProblemType;
-
-/* type of math operation used in an arithmetic question */
-typedef enum _MC_Operation {
- MC_OPER_ADD,
- MC_OPER_SUB,
- MC_OPER_MULT,
- MC_OPER_DIV,
- MC_NUM_OPERS
-} MC_Operation;
-
-/* math question formats: */
-typedef enum _MC_Format {
- MC_FORMAT_ANS_LAST, /* a + b = ? */
- MC_FORMAT_ANS_FIRST, /* ? + b = c */
- MC_FORMAT_ANS_MIDDLE, /* a + ? = c */
- MC_NUM_FORMATS
-} MC_Format;
-
-
-/*
-Indices for the various integer options. These are NOT the actual values!
-Actual values are accessed as such: options.iopts[PLAY_THROUGH_LIST] = val;
-Creating additional [integral] options is now centralized--it should only
-be necessary to add to this list, the list of text, and the list of
-defaults. (Besides actually using the new options!)
-*/
-enum {
- NOT_VALID_OPTION = -1 ,
- PLAY_THROUGH_LIST = 0 , /* play until all questions answered correctly */
- QUESTION_COPIES , /* # times each question is put in list */
- REPEAT_WRONGS , /* reuse incorrectly answered questions or not */
- COPIES_REPEATED_WRONGS , /* how many copies of an incorrectly answered question to re-insert*/
- ALLOW_NEGATIVES ,
- MAX_ANSWER ,
- MAX_QUESTIONS ,
- MAX_FORMULA_NUMS ,
- MIN_FORMULA_NUMS ,
-
- //NOTE: Do _not_ rearrange the FORMAT values because the functions
- //rely on index arithmetic to iterate through these, and will be
- //broken if the relative position changes!
- FORMAT_ANSWER_LAST , /* question format is: a + b = ? */
- FORMAT_ANSWER_FIRST , /* question format is: ? + b = c */
- FORMAT_ANSWER_MIDDLE , /* question format is: a + ? = c */
- FORMAT_ADD_ANSWER_LAST , /* a + b = ? */
- FORMAT_ADD_ANSWER_FIRST , /* ? + b = c */
- FORMAT_ADD_ANSWER_MIDDLE , /* a + ? = c */
- FORMAT_SUB_ANSWER_LAST , /* a - b = ? */
- FORMAT_SUB_ANSWER_FIRST , /* ? - b = c */
- FORMAT_SUB_ANSWER_MIDDLE , /* a - ? = c */
- FORMAT_MULT_ANSWER_LAST , /* a * b = ? */
- FORMAT_MULT_ANSWER_FIRST , /* ? * b = c */
- FORMAT_MULT_ANSWER_MIDDLE , /* a * ? = c */
- FORMAT_DIV_ANSWER_LAST , /* a / b = ? */
- FORMAT_DIV_ANSWER_FIRST , /* ? / b = c */
- FORMAT_DIV_ANSWER_MIDDLE , /* a / ? = c */
-
- ADDITION_ALLOWED ,
- SUBTRACTION_ALLOWED ,
- MULTIPLICATION_ALLOWED ,
- DIVISION_ALLOWED ,
- TYPING_PRACTICE_ALLOWED ,
- ARITHMETIC_ALLOWED ,
- COMPARISON_ALLOWED ,
-
- MIN_AUGEND , /* augend + addend = sum */
- MAX_AUGEND ,
- MIN_ADDEND ,
- MAX_ADDEND ,
-
- MIN_MINUEND , /* minuend - subtrahend = difference */
- MAX_MINUEND ,
- MIN_SUBTRAHEND ,
- MAX_SUBTRAHEND ,
-
- MIN_MULTIPLIER , /* multiplier * multiplicand = product */
- MAX_MULTIPLIER ,
- MIN_MULTIPLICAND ,
- MAX_MULTIPLICAND ,
-
- MIN_DIVISOR , /* dividend/divisor = quotient */
- MAX_DIVISOR , /* note - generate_list() will prevent */
- MIN_QUOTIENT , /* questions with division by zero. */
- MAX_QUOTIENT ,
-
- MIN_TYPING_NUM , /* range for "typing tutor" mode, for */
- MAX_TYPING_NUM , /* kids just learning to use keyboard. */
-
- MIN_COMPARATOR , /* left comparison operand */
- MAX_COMPARATOR ,
- MIN_COMPARISAND , /* right comparison operannd */
- MAX_COMPARISAND ,
-
- RANDOMIZE , /* whether to shuffle cards */
-
- COMPREHENSIVE , /* whether to generate all questions 'in order' */
- AVG_LIST_LENGTH , /* the average number of questions in a list */
- VARY_LIST_LENGTH , /* whether to randomly adjust list length */
-
- NOPTS
-};
-
-extern const char* const MC_OPTION_TEXT[];
-extern const int MC_DEFAULTS[];
-extern const char operchars[MC_NUM_OPERS];
-
-/* default values for math_options */
-#define MC_MAX_DIGITS 3
-#define MC_GLOBAL_MAX 999 /* This is the largest absolute value that */
- /* can be entered for math question values.*/
-#define MC_MATH_OPTS_INVALID -9999 /* Return value for accessor functions */
- /* if math_opts not valid */
-//#define DEFAULT_FRACTION_TO_KEEP 1
-
-
-/* FIXME I think this array-based options system is *much* more error-prone */
-/* and confusing than the old struct with a named field for each option, */
-/* albeit more compact. I think it would be worth the effort to change the */
-/* code back to the old system - DSB */
-typedef struct _MC_Options
-{
- int iopts[NOPTS];
-} MC_Options;
-
-
-
-/* struct for node in math "flashcard" list */
-typedef struct MC_MathQuestion {
- MC_FlashCard card;
- struct MC_MathQuestion* next;
- struct MC_MathQuestion* previous;
- int randomizer;
-} MC_MathQuestion;
-
-
-/* "public" function prototypes: these functions are how */
-/* a user interface communicates with MathCards: */
-/* TODO provide comments thoroughly explaining these functions */
-
-/* MC_Initialize() sets up the struct containing all of */
-/* settings regarding math questions. It should be */
-/* called before any other function. Many of the other */
-/* functions will not work properly if MC_Initialize() */
-/* has not been called. It only needs to be called once, */
-/* i.e when the program is starting, not at the beginning*/
-/* of each math game for the player. Returns 1 if */
-/* successful, 0 otherwise. */
-int MC_Initialize(void);
-
-/* MC_StartGame() generates the list of math questions */
-/* based on existing settings. It should be called at */
-/* the beginning of each math game for the player. */
-/* Returns 1 if resultant list contains 1 or more */
-/* questions, 0 if list empty or not generated */
-/* successfully. */
-int MC_StartGame(void);
-
-/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
-/* but uses the incorrectly answered questions from the */
-/* previous game for the question list as a review form */
-/* of learning. If there were no wrong answers (or no */
-/* previous game), it behaves just like MC_StartGame(). */
-/* FIXME wonder if it should generate a message if the */
-/* list is created from settings because there is no */
-/* valid wrong question list? */
-int MC_StartGameUsingWrongs(void);
-
-/* MC_NextQuestion() takes a pointer to an allocated */
-/* MC_MathQuestion struct and fills in the fields for */
-/* use by the user interface program. It basically is */
-/* like taking the next flashcard from the pile. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-/* or if argument pointer is invalid */
-int MC_NextQuestion(MC_FlashCard* q);
-
-/* MC_AnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has been answered */
-/* correctly, and how long the student took to answer. */
-/* Returns 1 if no errors. */
-int MC_AnsweredCorrectly(int id, float t);
-//int MC_AnsweredCorrectly_id(int id);
-
-/* MC_NotAnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has not been */
-/* answered correctly. Returns 1 if no errors. */
-int MC_NotAnsweredCorrectly(int id);
-
-/* Like MC_NextQuestion(), but takes "flashcard" from */
-/* pile of incorrectly answered questions. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-int MC_NextWrongQuest(MC_FlashCard* q);
-
-/* Returns 1 if all have been answered correctly, */
-/* 0 otherwise. */
-int MC_MissionAccomplished(void);
-
-/* Returns number of questions left (either in list */
-/* or "in play") */
-int MC_TotalQuestionsLeft(void);
-
-/* Returns questions left in list, NOT */
-/* including questions currently "in play". */
-int MC_ListQuestionsLeft(void);
-
-/* To keep track of how long students take to answer the */
-/* questions, one can report the time needed to answer */
-/* an individual question: */
-int MC_AddTimeToList(float t);
-/* Note that initialization of the list is handled by */
-/* MC_StartGame. */
-
-/* Tells MathCards to clean up - should be called when */
-/* user interface program exits. */
-void MC_EndGame(void);
-
-/* Prints contents of math_opts struct in human-readable */
-/* form to given file. "verbose" tells the function to */
-/* write a lot of descriptive "help"-type info for each */
-/* option (intended to make config files self-documenting).*/
-void MC_PrintMathOptions(FILE* fp, int verbose);
-
-/* Additional functions used to generate game summary files: */
-int MC_PrintQuestionList(FILE* fp);
-int MC_PrintWrongList(FILE* fp);
-int MC_StartingListLength(void);
-int MC_WrongListLength(void);
-int MC_NumAnsweredCorrectly(void);
-int MC_NumNotAnsweredCorrectly(void);
-float MC_MedianTimePerQuestion(void);
-void print_card(MC_FlashCard card);
-
-/********************************************
-Public functions for new mathcards architecture
-*********************************************/
-/* Return the array index of the given text, e.g. randomize->47 */
-unsigned int MC_MapTextToIndex(const char* text);
-void MC_SetOpt(unsigned int index, int val); //set an option
-int MC_GetOpt(unsigned int index); //get an option :)
-int MC_VerifyOptionListSane(void);
-int MC_MaxFormulaSize(void); //amount of memory needed to safely hold strings
-int MC_MaxAnswerSize(void);
-MC_FlashCard MC_AllocateFlashcard();
-void MC_CopyCard(const MC_FlashCard* src, MC_FlashCard* dest);
-void MC_FreeFlashcard(MC_FlashCard* fc);
-void MC_ResetFlashCard(MC_FlashCard* fc); //empty flashcard of strings & values
-int MC_FlashCardGood(const MC_FlashCard* fc); //verifies a flashcard is valid
-/* Reorganize formula_string and answer_string to render the same equation
- in a different format */
-void reformat_arithmetic(MC_FlashCard* card, MC_Format f);
-
-#endif
diff --git a/src/menu.c b/src/menu.c
index 770a950..5771e4b 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -315,14 +315,6 @@ int handle_activity(int act, int param)
run_arcade(param);
break;
- case RUN_LAN_HOST:
- run_lan_host();
- break;
-
- case RUN_LAN_JOIN:
- run_lan_join();
- break;
-
case RUN_CUSTOM:
run_custom_game();
break;
@@ -573,140 +565,6 @@ int run_factoroids(int choice)
}
-/* If pthreads available, we launch server in own thread. Otherwise, we use */
-/* the C system() call to launch the server as a standalone program. */
-int run_lan_host(void)
-{
-#ifdef HAVE_LIBSDL_NET
- char buf[256];
- char server_name[150];
- char* argv[3];
- int chosen_lesson = -1;
-
- /* For now, only allow one server instance: */
- if(ServerRunning())
- {
- ShowMessage(DEFAULT_MENU_FONT_SIZE, _("The server is already running"),
- NULL, NULL, NULL);
- return 0;
- }
-
- NameEntry(server_name, _("Enter Server Name:"), _("(limit 50 characters)"));
- argv[0] = "tuxmathserver";
- argv[1] = "--name";
- snprintf(buf, 256, "\"%s\"", server_name);
- argv[2] = buf;
-
-
- /* If we have POSIX threads available (Linux), we launch server in a thread within */
- /* our same process. The server will use the currently selected Mathcards settings, */
- /* so we can let the user select the lesson for the server to use. */
-
-#ifdef HAVE_PTHREAD_H
-
- ShowMessage(DEFAULT_MENU_FONT_SIZE,
- _("Click or press key to select server lesson file"),
- NULL, NULL, NULL);
-
- {
- chosen_lesson = run_menu(menus[MENU_LESSONS], true);
-
- while (chosen_lesson >= 0)
- {
- if (Opts_GetGlobalOpt(MENU_SOUND))
- playsound(SND_POP);
-
- /* Re-read global settings first in case any settings were */
- /* clobbered by other lesson or arcade games this session: */
- read_global_config_file();
- /* Now read the selected file and play the "mission": */
- if (read_named_config_file(lesson_list_filenames[chosen_lesson]))
- break;
- else
- { // Something went wrong - could not read lesson config file:
- fprintf(stderr, "\nCould not find file: %s\n", lesson_list_filenames[chosen_lesson]);
- chosen_lesson = -1;
- }
- // Let the user choose another lesson; start with the screen and
- // selection that we ended with
- chosen_lesson = run_menu(menus[MENU_LESSONS], true);
- }
- }
-
- ShowMessage(DEFAULT_MENU_FONT_SIZE,
- _("Server Name:"),
- server_name,
- _("Selected Lesson:"),
- lesson_list_titles[chosen_lesson]);
-
- RunServer_pthread(3, argv);
-
-
- /* Without pthreads, we just launch standalone server, which for now only */
- /* supports the hardcoded default settings. */
-#else
- RunServer_prog(3, argv);
-#endif
-
-/* No SDL_net, so show explanatory message: */
-#else
- ShowMessage(DEFAULT_MENU_FONT_SIZE,
- NULL, _("Sorry, this version built without network support"), NULL, NULL);
- printf( _("Sorry, this version built without network support.\n"));
-#endif
- return 0;
-}
-
-
-
-int run_lan_join(void)
-{
-#ifdef HAVE_LIBSDL_NET
- if(detecting_servers(_("Detecting servers"), _("Please wait")))
- {
- int stdby;
- char buf[256];
- char player_name[HIGH_SCORE_NAME_LENGTH * 3];
-
- snprintf(buf, 256, _("Connected to server: %s"), LAN_ConnectedServerName());
- NameEntry(player_name, buf, _("Enter your name:"));
- LAN_SetName(player_name);
- Ready(_("Click when ready"));
- LAN_StartGame();
- stdby = Standby(_("Waiting for other players"), NULL);
- if (stdby == 1)
- {
- audioMusicUnload();
- Opts_SetLanMode(1); // Tells game() we are playing over network
- game();
- Opts_SetLanMode(0); // Go back to local play
- if (Opts_GetGlobalOpt(MENU_MUSIC))
- audioMusicLoad( "tuxi.ogg", -1 );
- }
- else
- {
- ShowMessage(DEFAULT_MENU_FONT_SIZE,
- NULL, _("Sorry, game already in progress."), NULL, NULL);
- printf(_("Sorry, game already in progress.\n"));
- }
- }
- else
- {
- ShowMessage(DEFAULT_MENU_FONT_SIZE,
- NULL, _("Sorry, no server could be found."), NULL, NULL);
- printf(_("Sorry, no server could be found.\n"));
- }
-#else
- ShowMessage(DEFAULT_MENU_FONT_SIZE,
- NULL, _("Sorry, this version built without network support"), NULL, NULL);
- printf( _("Sorry, this version built without network support.\n"));
-#endif
-
- DEBUGMSG(debug_menu, "Leaving run_lan_join()\n");
- return 0;
-}
-
-
/* Display the menu and run the event loop.
if return_choice = true then return chosen value instead of
running handle_activity()
@@ -1454,11 +1312,11 @@ void LoadMenus(void)
menu_file = fopen(DATA_PREFIX "/menus/main_menu.xml", "r");
if(menu_file == NULL)
{
- DEBUGMSG(debug_menu, "LoadMenus(): Could not load main menu file !\n");
+ DEBUGMSG(debug_menu, "LoadMenus(): Could not load main menu file !\n");
}
else
{
- menus[MENU_MAIN] = load_menu_from_file(menu_file, NULL);
+ menus[MENU_MAIN] = load_menu_from_file(menu_file, NULL);
fclose(menu_file);
}
diff --git a/src/menu.h b/src/menu.h
index 4787476..9dd2732 100644
--- a/src/menu.h
+++ b/src/menu.h
@@ -37,8 +37,6 @@
X( RUN_ARCADE ),\
X( RUN_CUSTOM ),\
X( RUN_MAIN_MENU ),\
- X( RUN_LAN_HOST ),\
- X( RUN_LAN_JOIN ),\
X( RUN_SCORE_SWEEP ),\
X( RUN_ELIMINATION ),\
X( RUN_FACTORS ),\
diff --git a/src/multiplayer.c b/src/multiplayer.c
deleted file mode 100644
index 496b053..0000000
--- a/src/multiplayer.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
-
-multiplayer.h - Provides routines for organizing and running a turn-based
- multiplayer that can accommodate up to four players (more with
- a recompilation)
-
-Author: B. Luchen
-
-*/
-
-#include "tuxhistory.h"
-#include "SDL_extras.h"
-#include "multiplayer.h"
-#include "game.h"
-#include "options.h"
-#include "fileops.h"
-#include "highscore.h"
-#include "credits.h"
-
-int params[NUM_PARAMS] = {0, 0, 0, 0};
-
-int inprogress = 0;
-int pscores[MAX_PLAYERS];
-char* pnames[MAX_PLAYERS];
-
-//local function decs
-static void showWinners(int* order, int num); //show a sequence recognizing winner
-static int initMP();
-static void cleanupMP();
-
-void mp_set_parameter(unsigned int param, int value)
-{
- if (inprogress)
- {
- DEBUGMSG(debug_multiplayer, "Oops, tried to set param %d in the middle of a game\n", param);
- return;
- }
- params[param] = value;
-}
-
-void mp_run_multiplayer()
-{
- int i;
- int round = 1;
- int currentplayer = 0;
- int result = 0;
- int done = 0;
- int activeplayers = params[PLAYERS];
- int winners[MAX_PLAYERS];
-
- for (i = 0; i < MAX_PLAYERS; ++i)
- winners[i] = -1;
-
- if (initMP() )
- {
- printf("Initialization failed, bailing out\n");
- return;
- }
-
- //cycle through players until all but one has lost
- if (params[MODE] == ELIMINATION)
- {
- while(!done)
- {
- //TODO maybe gradually increase difficulty
- game_set_start_message(pnames[currentplayer], "Go!", "", "");
- result = game();
-
- if (result == GAME_OVER_LOST || result == GAME_OVER_ESCAPE)
- {
- //eliminate player
- pscores[currentplayer] = 0xbeef;
- winners[--activeplayers] = currentplayer;
- }
-
- do //move to the next player
- {
- ++currentplayer;
- currentplayer %= params[PLAYERS];
- if (currentplayer == 0)
- ++round;
- }
- while (pscores[currentplayer] == 0xbeef); //skip over eliminated players
-
- if (activeplayers <= 1) //last man standing!
- {
- DEBUGMSG(debug_multiplayer, "%d wins\n", currentplayer);
- winners[0] = currentplayer;
- done = 1;
- }
- }
- }
- //players take turns, accumulating score, and the highest score wins
- else if (params[MODE] == SCORE_SWEEP)
- {
- int hiscore = 0;
- int currentwinner = -1;
-
- //play through rounds
- for (round = 1; round <= params[ROUNDS]; ++round)
- {
- for (currentplayer = 0; currentplayer < params[PLAYERS]; ++currentplayer)
- {
- game_set_start_message(pnames[currentplayer], "Go!", NULL, NULL);
- result = game();
- pscores[currentplayer] += Opts_LastScore(); //add this player's score
- if (result == GAME_OVER_WON)
- pscores[currentplayer] += 500; //plus a possible bonus
- }
- }
-
- //sort out winners
- for (i = 0; i < params[PLAYERS]; ++i)
- {
- hiscore = 0;
- for (currentplayer = 0; currentplayer < params[PLAYERS]; ++currentplayer)
- {
- if (pscores[currentplayer] >= hiscore)
- {
- hiscore = pscores[currentplayer];
- currentwinner = currentplayer;
- }
- winners[i] = currentwinner;
- pscores[currentwinner] = -1;
- }
- }
- }
-
- DEBUGMSG(debug_multiplayer, "Game over; showing winners\n");
-
- showWinners(winners, params[PLAYERS]);
- cleanupMP();
-}
-
-int mp_get_player_score(int playernum)
-{
- if (playernum > params[PLAYERS])
- {
- DEBUGMSG(debug_multiplayer, "No player %d!\n", playernum);
- return 0;
- }
- return pscores[playernum];
-}
-
-const char* mp_get_player_name(int playernum)
-{
- if (playernum > params[PLAYERS])
- {
- DEBUGMSG(debug_multiplayer, "No player %d!\n", playernum);
- return 0;
- }
- return pnames[playernum];
-}
-
-int mp_get_parameter(unsigned int param)
-{
- if (param > NUM_PARAMS)
- {
- printf("Invalid mp_param index: %d\n", param);
- return 0;
- }
- return params[param];
-}
-
-//TODO a nicer-looking sequence that also recognizes second place etc.
-void showWinners(int* winners, int num)
-{
- int skip = 0;
- int i = 0;
- const int boxspeed = 3;
- int sectionlength = num * (HIGH_SCORE_NAME_LENGTH + strlen(" wins!\n"));
- char text[sectionlength];
- SDL_Rect box = {screen->w / 2, screen->h / 2, 0, 0};
- SDL_Rect center = box;
- SDL_Event evt;
-
- const char* winnername = (winners[0] == -1 ? "Nobody" : pnames[winners[0]] );
-
- snprintf(text, HIGH_SCORE_NAME_LENGTH + strlen(" wins!"),
- "%s wins!\n", winnername);
- for (i = 1; i < num; ++i)
- {
- snprintf(strchr(text, '\0'), sectionlength, _("Then %s\n"), pnames[winners[i]]);
- }
-
- DEBUGMSG(debug_multiplayer, "%s Win text: %s\n", pnames[winners[0]], text);
-
- DarkenScreen(1);
-
- while (box.h < screen->h || box.w < screen->w)
- {
- //expand black box
- box.x -= boxspeed;
- box.y -= boxspeed;
- box.h += boxspeed * 2;
- box.w += boxspeed * 2;
-
- //reveal text specifying the winner
- SDL_FillRect(screen, &box, 0);
- draw_text(text, center);
- SDL_UpdateRect(screen, box.x, box.y, box.w, box.h);
-
- while (SDL_PollEvent(&evt) )
- if (evt.type == SDL_KEYDOWN && evt.key.keysym.sym == SDLK_ESCAPE)
- skip = 1;
- if (skip)
- break;
- SDL_Delay(50);
- }
- //in case we've skipped, cover the whole screen
- SDL_FillRect(screen, NULL, 0);
- draw_text(text, center);
- SDL_Flip(screen);
- WaitForEvent(SDL_KEYDOWNMASK | SDL_MOUSEBUTTONDOWNMASK);
-}
-
-int initMP()
-{
- int i;
- int success = 1;
- char nrstr[HIGH_SCORE_NAME_LENGTH * 3];
- int nplayers = params[PLAYERS];
-
- const char* config_files[5] = {
- "multiplay/space_cadet",
- "multiplay/scout",
- "multiplay/ranger",
- "multiplay/ace",
- "multiplay/commando"
- };
-
- DEBUGMSG(debug_multiplayer, "Reading in difficulty settings...\n");
-
- success *= read_global_config_file();
-
- success *= read_named_config_file("multiplay/mpoptions");
-
- success *= read_named_config_file(config_files[params[DIFFICULTY]]);
-
- if (!success)
- {
- printf("Couldn't read in settings for %s\n",
- config_files[params[DIFFICULTY]] );
- return 1;
- }
-
- pscores[0] = pscores[1] = pscores[2] = pscores[3] = 0;
- pnames[0] = pnames[1] = pnames[2] = pnames[3] = NULL;
-
- //allocate and enter player names
- for (i = 0; i < nplayers; ++i)
- pnames[i] = malloc((1 + 3 * HIGH_SCORE_NAME_LENGTH) * sizeof(char) );
- for (i = 0; i < nplayers; ++i)
- {
- if (pnames[i])
- {
- if (i == 0) //First player
- NameEntry(pnames[i], N_("Who is playing first?"), N_("Enter your name:"));
- else //subsequent players
- NameEntry(pnames[i], N_("Who is playing next?"), N_("Enter your name:"));
- }
- else
- {
- printf("Can't allocate name %d!\n", i);
- return 1;
- }
- }
-
- //enter how many rounds
- if (params[MODE] == SCORE_SWEEP)
- {
- while (params[ROUNDS] <= 0)
- {
- NameEntry(nrstr, N_("How many rounds will you play?"), N_("Enter a number"));
- params[ROUNDS] = atoi(nrstr);
- }
- }
- inprogress = 1; //now we can start the game
- return 0;
-}
-
-void cleanupMP()
-{
- int i;
-
- for (i = 0; i < params[PLAYERS]; ++i)
- if (pnames[i])
- free(pnames[i]);
-
- for (i = 0; i < NUM_PARAMS; ++i)
- params[i] = 0;
-
- inprogress = 0;
-}
diff --git a/src/multiplayer.h b/src/multiplayer.h
deleted file mode 100644
index 0901dfd..0000000
--- a/src/multiplayer.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef MULTIPLAYER_H
-#define MULTIPLAYER_H
-
-/*
-
-multiplayer.h - Provides routines for organizing and running a turn-based
- multiplayer that can accommodate up to four players (more with
- a recompilation)
-
-Author: B. Luchen
-
-*/
-
-#define MAX_PLAYERS 4
-
-enum {
- PLAYERS,
- ROUNDS,
- DIFFICULTY,
- MODE,
- NUM_PARAMS
-};
-
-typedef enum {
- SCORE_SWEEP,
- ELIMINATION
-} MP_Mode;
-
-void mp_set_parameter(unsigned int param, int value);
-int mp_get_parameter(unsigned int param);
-void mp_run_multiplayer();
-int mp_get_player_score(int playernum);
-const char* mp_get_player_name(int playernum);
-int mp_num_players();
-
-#endif // MULTIPLAYER_H
diff --git a/src/network.c b/src/network.c
deleted file mode 100644
index 15af597..0000000
--- a/src/network.c
+++ /dev/null
@@ -1,509 +0,0 @@
-/*
-* C Implementation: network.c
-*
-* Description: Contains all the network-related functions for
-* LAN-based play in "Tux, of Math Command".
-*
-*
-* Author: Akash Gangil, David Bruce, and the TuxMath team, (C) 2009
-* Developers list: <tuxmath-devel at lists.sourceforge.net>
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*/
-
-/* Must have this first for the #ifdef HAVE_LIBSDL_NET to work */
-#include "globals.h"
-
-#ifdef HAVE_LIBSDL_NET
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "mathcards.h"
-#include "transtruct.h"
-#include "network.h"
-#include "throttle.h"
-//#include "testclient.h"
-
-
-TCPsocket sd; /* Server socket descriptor */
-SDLNet_SocketSet set;
-IPaddress serv_ip;
-ServerEntry servers[MAX_SERVERS];
-static int connected_server = -1;
-
-/* Local function prototypes: */
-int say_to_server(char *statement);
-int evaluate(char *statement);
-int add_to_server_list(UDPpacket* pkt);
-
-
-int LAN_DetectServers(void)
-{
- UDPsocket udpsock = NULL;
- UDPpacket* out;
- UDPpacket* in;
- IPaddress bcast_ip;
- int sent = 0;
- int done = 0;
- int seconds = 0;
- int num_servers = 0;
- int i = 0;
- Uint32 timer = 0;
- //zero out old server list
- for(i = 0; i < MAX_SERVERS; i++)
- servers[i].ip.host = 0;
-
- /* Docs say we are supposed to call SDL_Init() before SDLNet_Init(): */
- if(SDL_Init(0) == -1)
- {
- printf("SDL_Init: %s\n", SDL_GetError());
- return 0;;
- }
-
- /* Initialize SDL_net */
- if (SDLNet_Init() < 0)
- {
- fprintf(stderr, "SDLNet_Init: %s\n", SDLNet_GetError());
- exit(EXIT_FAILURE);
- }
-
- //NOTE we can't open a UDP socket on the same port if both client
- //and server are running on the same machine, so for now we let
- //it be auto-assigned:
- udpsock = SDLNet_UDP_Open(0);
- if(!udpsock)
- {
- printf("SDLNet_UDP_Open: %s\n", SDLNet_GetError());
- return 0;
- }
-
- out = SDLNet_AllocPacket(NET_BUF_LEN);
- in = SDLNet_AllocPacket(NET_BUF_LEN);
-
- SDLNet_ResolveHost(&bcast_ip, "255.255.255.255", DEFAULT_PORT);
- out->address.host = bcast_ip.host;
- sprintf(out->data, "TUXMATH_CLIENT");
- out->address.port = bcast_ip.port;
- out->len = strlen("TUXMATH_CLIENT") + 1;
-
- //Here we will need to send every few seconds until we hear back from server
- //and get its ip address: IPaddress bcast_ip;
- printf("\nAutodetecting TuxMath servers:");
- fflush(stdout);
- while(!done)
- {
- printf(".");
- fflush(stdout);
-
- sent = SDLNet_UDP_Send(udpsock, -1, out);
- if(!sent)
- {
- printf("broadcast failed - network inaccessible.\nTrying localhost (for testing)\n");
- SDLNet_ResolveHost(&bcast_ip, "localhost", DEFAULT_PORT);
- out->address.host = bcast_ip.host;
- }
- SDL_Delay(250); //give server chance to answer
-
- while(SDLNet_UDP_Recv(udpsock, in))
- {
- if(strncmp((char*)in->data, "TUXMATH_SERVER", strlen("TUXMATH_SERVER")) == 0)
- {
- done = 1;
- //add to list, checking for duplicates
- num_servers = add_to_server_list(in);
- }
- }
- //Make sure we always scan at least one but not more than five seconds:
- Throttle(1000, &timer); //repeat once per second
- seconds++;
- if(seconds < 1)
- done = 0;
- if(seconds > 5)
- done = 1;
-
- }
-
- printf("done\n\n");
-
- SDLNet_FreePacket(out);
- SDLNet_FreePacket(in);
- print_server_list();
- return num_servers;
-}
-
-
-char* LAN_ServerName(int i)
-{
- if(i < 0 || i > MAX_SERVERS)
- return NULL;
- if(servers[i].ip.host != 0)
- return servers[i].name;
- else
- return NULL;
-}
-
-char* LAN_ConnectedServerName(void)
-{
- return servers[connected_server].name;
-}
-
-//For the simple case where a single server is found, i is
-//always 0. Otherwise the player has to review the choices
-//via LAN_ServerName(i) to get the index
-int LAN_AutoSetup(int i)
-{
- if(i < 0 || i > MAX_SERVERS)
- return 0;
-
- /* Open a connection based on autodetection routine: */
- if (!(sd = SDLNet_TCP_Open(&servers[i].ip)))
- {
- fprintf(stderr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError());
- return 0;
- }
-
- /* We create a socket set so we can check for activity: */
- set = SDLNet_AllocSocketSet(1);
- if(!set)
- {
- printf("SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
- return 0;
- }
-
- if(SDLNet_TCP_AddSocket(set, sd) == -1)
- {
- printf("SDLNet_AddSocket: %s\n", SDLNet_GetError());
- // perhaps you need to restart the set and make it bigger...
- }
-
- // Success - record the index for future reference:
- connected_server = i;
- return 1;
-}
-
-
-
-// int LAN_Setup(char *host, int port)
-// {
-// IPaddress ip; /* Server address */
-//
-// if(SDL_Init(0)==-1)
-// {
-// printf("SDL_Init: %s\n", SDL_GetError());
-// return 0;;
-// }
-//
-// if (SDLNet_Init() < 0)
-// {
-// fprintf(stderr, "SDLNet_Init: %s\n", SDLNet_GetError());
-// return 0;
-// }
-//
-// /* Resolve the host we are connecting to */
-// if (SDLNet_ResolveHost(&ip, host, port) < 0)
-// {
-// fprintf(stderr, "SDLNet_ResolveHost: %s\n", SDLNet_GetError());
-// return 0;
-// }
-//
-// /* Open a connection with the IP provided (listen on the host's port) */
-// if (!(sd = SDLNet_TCP_Open(&ip)))
-// {
-// fprintf(stderr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError());
-// return 0;
-// }
-//
-// /* We create a socket set so we can check for activity: */
-// set = SDLNet_AllocSocketSet(1);
-// if(!set)
-// {
-// printf("SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
-// return 0;
-// }
-//
-// if(SDLNet_TCP_AddSocket(set, sd) == -1)
-// {
-// printf("SDLNet_AddSocket: %s\n", SDLNet_GetError());
-// // perhaps you need to restart the set and make it bigger...
-// }
-//
-//
-// return 1;
-// }
-
-
-void LAN_Cleanup(void)
-{
- if(sd)
- {
- SDLNet_TCP_Close(sd);
- sd = NULL;
- }
-
- if(set)
- {
- SDLNet_FreeSocketSet(set);
- set = NULL;
- }
- SDLNet_Quit();
-}
-
-
-
-int LAN_SetName(char* name)
-{
- char buf[NET_BUF_LEN];
- if(!name)
- return 0;
- snprintf(buf, NET_BUF_LEN, "%s\t%s", "SET_NAME", name);
- return say_to_server(buf);
-}
-
-
-
-
-
-
-/* Appears a return value of 0 means message received, 1 means no socket activity */
-int check_messages(char buf[NET_BUF_LEN])
-{
- int numready;
-
- //This is supposed to check to see if there is activity:
- numready = SDLNet_CheckSockets(set, 0);
- if(numready == -1)
- {
- printf("SDLNet_CheckSockets: %s\n", SDLNet_GetError());
- //most of the time this is a system error, where perror might help you.
- perror("SDLNet_CheckSockets");
- return -1;
- }
- else if(numready > 0)
- {
- // check socket with SDLNet_SocketReady and handle if active:
- if(SDLNet_SocketReady(sd))
- {
- buf[0] = '\0';
- if(SDLNet_TCP_Recv(sd, buf, NET_BUF_LEN) <= 0)
- {
- fprintf(stderr, "In check_messages(), SDLNet_TCP_Recv() failed!\n");
- return -1;
- }
- return 0;
- }
- }
- return 1;
-}
-
-
-/* Here we get the next message from the server if one is available. */
-/* We return 1 if a message received, 0 if no activity, -1 on errors */
-/* or if connection is lost: */
-int LAN_NextMsg(char* buf)
-{
- int numready = 0;
-
- /* Make sure we have place to put message: */
- if(buf == NULL)
- {
- printf("get_next_msg() passed NULL buffer\n");
- return -1;
- }
-
- //Check to see if there is socket activity:
- numready = SDLNet_CheckSockets(set, 0);
- if(numready == -1)
- {
- printf("SDLNet_CheckSockets: %s\n", SDLNet_GetError());
- //most of the time this is a system error, where perror might help you.
- perror("SDLNet_CheckSockets");
- return -1;
- }
- else if(numready > 0)
- {
- // check with SDLNet_SocketReady():
- if(SDLNet_SocketReady(sd))
- {
- buf[0] = '\0';
-
- if(SDLNet_TCP_Recv(sd, buf, NET_BUF_LEN) > 0)
- {
- //Success - message is now in buffer
- return 1;
- }
- else
- {
- fprintf(stderr, "In get_next_msg(), SDLNet_TCP_Recv() failed!\n");
- SDLNet_TCP_DelSocket(set, sd);
- if(sd != NULL)
- SDLNet_TCP_Close(sd);
- sd = NULL;
- return -1;
- }
- }
- else
- {
- fprintf(stderr, "In get_next_msg(), socket set reported active but no activity found\n");
- SDLNet_TCP_DelSocket(set, sd);
- if(sd != NULL)
- SDLNet_TCP_Close(sd);
- sd = NULL;
- return -1;
- }
- }
- // No socket activity - just return 0:
- return 0;
-}
-
-
-
-
-int Make_Flashcard(char* buf, MC_FlashCard* fc)
-{
- int i = 0,tab = 0, s = 0;
- char formula[MC_FORMULA_LEN];
- sscanf (buf,"%*s%d%d%d%s",
- &fc->question_id,
- &fc->difficulty,
- &fc->answer,
- fc->answer_string); /* can't formula_string in sscanf in here cause it includes spaces*/
-
- /*doing all this cause sscanf will break on encountering space in formula_string*/
- /* NOTE changed to index notation so we keep within NET_BUF_LEN */
- while(buf[i]!='\n' && i < NET_BUF_LEN)
- {
- if(buf[i]=='\t')
- tab++;
- i++;
- if(tab == 5)
- break;
- }
-
- while((buf[i] != '\n')
- && (s < MC_FORMULA_LEN - 1)) //Must leave room for terminating null
- {
- formula[s] = buf[i] ;
- i++;
- s++;
- }
- formula[s]='\0';
- strcpy(fc->formula_string, formula);
-
- DEBUGMSG(debug_lan, "In Make_Flashcard, new card is:\n");
- DEBUGCODE(debug_lan) print_card(*fc);
-
-return 1;
-}
-
-
-int LAN_StartGame(void)
-{
- char buffer[NET_BUF_LEN];
- snprintf(buffer, NET_BUF_LEN, "%s", "START_GAME");
- return say_to_server(buffer);
-}
-
-
-int LAN_AnsweredCorrectly(int id, float t)
-{
- char buffer[NET_BUF_LEN];
- snprintf(buffer, NET_BUF_LEN, "%s\t%d\t%f", "CORRECT_ANSWER", id, t);
- return say_to_server(buffer);
-}
-
-
-int LAN_NotAnsweredCorrectly(int id)
-{
- char buffer[NET_BUF_LEN];
- snprintf(buffer, NET_BUF_LEN, "%s\t%d", "WRONG_ANSWER", id);
- return say_to_server(buffer);
-}
-
-
-/* This tells the server we are quitting the current math game, but */
-/* not disconnecting our socket: */
-int LAN_LeaveGame(void)
-{
- char buf[NET_BUF_LEN];
- snprintf(buf, NET_BUF_LEN, "%s", "LEAVE_GAME");
- return say_to_server(buf);
-}
-
-
-
-/*private to network.c functions*/
-
-int say_to_server(char* statement)
-{
- char buffer[NET_BUF_LEN];
-
- if(!statement)
- return 0;
-
- snprintf(buffer, NET_BUF_LEN,
- "%s",
- statement);
- if (SDLNet_TCP_Send(sd, (void *)buffer, NET_BUF_LEN) < NET_BUF_LEN)
- {
- fprintf(stderr, "SDLNet_TCP_Send: %s\n", SDLNet_GetError());
- return 0;
- }
-
- return 1;
-}
-
-//add name to list, checking for duplicates:
-int add_to_server_list(UDPpacket* pkt)
-{
- int i = 0;
- int already_in = 0;
- char* p = NULL;
-
- if(!pkt)
- return 0;
-
- //first see if it is already in list:
- while((i < MAX_SERVERS)
- && (servers[i].ip.host != 0))
- {
- if(pkt->address.host == servers[i].ip.host)
- already_in = 1;
- i++;
- }
-
- //Copy it in unless it's already there, or we are out of room:
- if(!already_in && i < MAX_SERVERS)
- {
- servers[i].ip.host = pkt->address.host;
- servers[i].ip.port = pkt->address.port;
- // not using sscanf() because server_name could contain whitespace:
- p = strchr(pkt->data, '\t');
- p++;
- if(p)
- strncpy(servers[i].name, p, NAME_SIZE);
-
- i++;
- }
-
- return i; //i should be the number of items in the list
-}
-
-void print_server_list(void)
-{
- int i = 0;
- printf("Detected servers:\n");
- while(i < MAX_SERVERS && servers[i].ip.host != 0)
- {
- printf("SERVER NUMBER %d: %s\n", i, servers[i].name);
- i++;
- }
-}
-
-
-
-#endif
diff --git a/src/network.h b/src/network.h
deleted file mode 100644
index 797385c..0000000
--- a/src/network.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-
- network.h
-
- Description: Provides routines for various networking functions to be used
- in the LAN multiplayer game.
- Author: David Bruce ,Akash Gangil and the TuxMath team, (C) 2009
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-
-
-
-
-#ifndef NETWORK_H
-#define NETWORK_H
-
-#include "config.h"
-
-#ifdef HAVE_LIBSDL_NET
-
-#include "transtruct.h"
-#include "SDL_net.h"
-
-
-typedef struct {
- IPaddress ip; /* 32-bit IPv4 host address */
- char name[NAME_SIZE];
-}ServerEntry;
-
-
-/* Networking setup and cleanup: */
-int LAN_DetectServers(void);
-int LAN_AutoSetup(int i);
-char* LAN_ServerName(int i);
-char* LAN_ConnectedServerName(void);
-void print_server_list(void);
-
-//int LAN_Setup(char* host, int port);
-void LAN_Cleanup(void);
-int LAN_SetName(char* name);
-
-/* Network replacement functions for mathcards "API": */
-/* These functions are how the client tells things to the server: */
-int LAN_StartGame(void);
-int LAN_AnsweredCorrectly(int id, float t);
-int LAN_NotAnsweredCorrectly(int id);
-int LAN_LeaveGame(void);
-/* This is how the client receives messages from the server: */
-int LAN_NextMsg(char* buf);
-
-/* NOTE probably won't have this in multiplayer - new quests determined by server */
-//int LAN_NextQuestion(void);
-
-
-
-/* FIXME appears this one is basically the same as LAN_NextMsg() */
-int check_messages(char *);
-/* FIXME this should be local to network.c */
-int Make_Flashcard(char* buf, MC_FlashCard* fc);
-
-#endif // HAVE_LIBSDL_NET
-
-#endif // NETWORK_H
diff --git a/src/server.c b/src/server.c
deleted file mode 100644
index db568b9..0000000
--- a/src/server.c
+++ /dev/null
@@ -1,1260 +0,0 @@
-/*
-* C Implementation: server.c
-*
-* Description: Server program for LAN-based play in Tux,of Math Command.
-*
-*
-* Author: Akash Gangil, David Bruce, and the TuxMath team, (C) 2009
-* Developers list: <tuxmath-devel at lists.sourceforge.net>
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*
-* NOTE: This file was initially based on example code from The Game Programming Wiki
-* (http://gpwiki.org), in a tutorial covered by the GNU Free Documentation License 1.2.
-* No invariant sections were indicated, and no separate license for the example code
-* was listed. The author was also not listed. AFAICT,this scenario allows incorporation of
-* derivative works into a GPLv2+ project like TuxMath. FWIW, virtually none of
-* the tutorial code is still present here - David Bruce
-*/
-
-/* This must come before #ifdef HAVE_LIBSDL_NET to get "config.h" */
-#include "globals.h"
-
-#ifdef HAVE_LIBSDL_NET
-
-#include "server.h"
-#include "transtruct.h"
-#include "mathcards.h"
-#include "throttle.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifdef HAVE_PTHREAD_H
-#include <pthread.h>
-#endif
-
-
-#define MAX_ARGS 16
-
-
-/* ----------- Local function prototypes: ------------ */
-
-// setup and cleanup:
-int setup_server(void);
-void cleanup_server(void);
-void server_handle_command_args(int argc, char* argv[]);
-void* run_server_local_args(void);
-
-// top level functions in main loop:
-void check_UDP(void);
-void update_clients(void);
-int server_check_messages(void);
-
-// client management utilities:
-int find_vacant_client(void);
-void remove_client(int i);
-void check_game_clients(void);
-
-// message reception:
-int handle_client_game_msg(int i, char* buffer);
-void handle_client_nongame_msg(int i, char* buffer);
-int msg_set_name(int i, char* buf);
-void start_game(void);
-void game_msg_correct_answer(int i, char* inbuf);
-void game_msg_wrong_answer(int i, char* inbuf);
-void game_msg_quit(int i);
-void game_msg_exit(int i);
-int calc_score(int difficulty, float t);
-
-//message sending:
-int add_question(MC_FlashCard* fc);
-int remove_question(int id);
-int send_counter_updates(void);
-int send_score_updates(void);
-//int SendQuestion(MC_FlashCard flash, TCPsocket client_sock);
-int SendMessage(int message, int ques_id, char* name, TCPsocket client_sock);
-int player_msg(int i, char* msg);
-void broadcast_msg(char* msg);
-int transmit(int i, char* msg);
-int transmit_all(char* msg);
-
-// For non-blocking input:
-int read_stdin_nonblock(char* buf, size_t max_length);
-
-
-// not really deprecated but not done in response to
-// client message --needs better name:
-void game_msg_next_question(void);
-
-
-
-/* ------------ "Local globals" for server.c: ---------- */
-char server_name[NAME_SIZE]; /* User-visible name for server selection */
-int need_server_name = 1;
-UDPsocket udpsock = NULL; /* Used to listen for client's server autodetection */
-TCPsocket server_sock = NULL; /* Socket descriptor for server to accept client TCP sockets. */
-IPaddress ip;
-SDLNet_SocketSet client_set = NULL, temp_set = NULL;
-static client_type client[MAX_CLIENTS];
-static int num_clients = 0;
-static int game_in_progress = 0;
-static int server_running = 0;
-static int quit = 0;
-MC_FlashCard flash;
-int local_argc;
-char* local_argv[MAX_ARGS];
-
-
-
-
-
-
-
-
-/* The previous contents of main() are wrapped into this function to */
-/* allow the server to be run as a function in a process or thread */
-/* within another program. main() is now in a separate file, */
-/* servermain.c, that consists solely of a call to RunServer(). */
-
-/* FIXME this isn't thread-safe - we need to return gracefully if we */
-/* find that the server is already running, instead of calling cleanup() */
-/* and crashing the program. Some of the setup and cleanup will have to */
-/* be called from main() rather than from here. */
-int RunServer(int argc, char* argv[])
-{
- Uint32 timer = 0;
-
- printf("Started tuxmathserver, waiting for client to connect:\n>\n");
-
- server_handle_command_args(argc, argv);
-
- /* ---------------- Setup: --------------------------- */
- if (!setup_server())
- {
- fprintf(stderr, "setup_server() failed - exiting.\n");
- cleanup_server();
- return EXIT_FAILURE;
- }
-
- server_running = 1;
-
- printf("Waiting for clients to connect:\n>");
- fflush(stdout);
-
- /* ------------- Main server loop: ------------------ */
- while (!quit)
- {
- /* Respond to any clients pinging us to find the server: */
- check_UDP();
- /* Now we check to see if anyone is trying to connect. */
- update_clients();
- /* Check for any pending messages from clients already connected: */
- server_check_messages();
-
- /* Limit frame rate to keep from eating all CPU: */
- /* NOTE almost certainly could make this longer wtihout noticably */
- /* affecting performance, but even throttling to 1 msec/loop cuts */
- /* CPU from 100% to ~2% on my desktop - DSB */
- Throttle(5, &timer); //min loop time 5 msec
- }
-
- server_running = 0;
-
- /* ----- Free resources before exiting: ------- */
- cleanup_server();
-
- return EXIT_SUCCESS;
-}
-
-/* If we can't use pthreads, we use this function */
-/* to launch the server as a separate program using */
-/* the C library system() call */
-int RunServer_prog(int argc, char* argv[])
-{
- char buf[256];
- int i;
-
- /* Construct command-line argument string from argc and argv: */
- /* NOTE this is not safe from buffer overflow - do */
- /* not use with user-supplied arguments. */
- snprintf(buf, 256, "tuxmathserver ");
- for(i = 1; i < argc; i++)
- {
- strncat(buf, argv[i], 256);
- strncat(buf, " ", 256);
- }
- /* Add '&' to make it non-blocking: */
- strncat(buf, "&", 256);
-
- return system(buf);
-}
-
-/*
- * This is the prefered way to run the tuxmath server,
- */
-
-#ifdef HAVE_PTHREAD_H
-int RunServer_pthread(int argc, char* argv[])
-{
- pthread_t server_thread;
- int i;
-
- /* We can only pass a single arg into the new thread, but it shares */
- /* the same address space, so we save argc and argv locally instead: */
- local_argc = argc;
- for(i = 0; i < argc && i < MAX_ARGS; i++)
- {
- local_argv[i] = argv[i];
- }
-
- if(pthread_create(&server_thread, NULL, run_server_local_args, NULL))
- {
- printf("Error creating thread\n");
- return -1;
- }
- return 0;
-}
-
-void* run_server_local_args(void)
-{
-
- RunServer(local_argc, local_argv);
- pthread_exit(NULL);
- return NULL;
-}
-
-#endif
-
-
-/*********************************************************************/
-/* "Private" (to server.c) functions */
-/*********************************************************************/
-
-
-/* ----- Setup and Cleanup: ------------------- */
-
-
-// setup_server() - all the things needed to get server running:
-int setup_server(void)
-{
- Uint32 timer = 0;
-
- //Initialize SDL and SDL_net:
- if(SDL_Init(0) == -1)
- {
- printf("SDL_Init: %s\n", SDL_GetError());
- return 0;;
- }
-
- if (SDLNet_Init() < 0)
- {
- fprintf(stderr, "SDLNet_Init: %s\n", SDLNet_GetError());
- return 0;
- }
-
-
- /* Resolving the host using NULL make network interface to listen */
- if (SDLNet_ResolveHost(&ip, NULL, DEFAULT_PORT) < 0)
- {
- fprintf(stderr, "SDLNet_ResolveHost: %s\n", SDLNet_GetError());
- return 0;
- }
-
- /* Open a connection with the IP provided (listen on the host's port) */
- if (!(server_sock = SDLNet_TCP_Open(&ip)))
- {
- fprintf(stderr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError());
- return 0;
- }
-
- client_set = SDLNet_AllocSocketSet(MAX_CLIENTS);
- if(!client_set)
- {
- printf("SDLNet_AllocSocketSet: %s\n", SDLNet_GetError());
- return 0;
- }
-
- //this sets up our mathcards "library" with hard-coded defaults - no
- //settings read from config file here as of yet:
- if (!MC_Initialize())
- {
- fprintf(stderr, "Could not initialize MathCards\n");
- return 0;
- }
-
- /* Get server name: */
- /* We use default name after 30 sec timeout if no name entered. */
- /* FIXME we should save this to disc so it doesn't */
- /* have to be entered every time. */
- if(need_server_name)
- {
- Uint32 timeout = SDL_GetTicks() + SERVER_NAME_TIMEOUT;
- int name_recvd = 0;
- server_name[0] = '\0';
-
- /* We can use fcntl() on Linux/Unix plaforms: */
-#ifdef HAVE_FCNTL
- fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK);
-
- printf("Enter the SERVER's NAME: \n>");
- fflush(stdout);
-
- while(!name_recvd && (SDL_GetTicks() < timeout))
- {
- if(read_stdin_nonblock(server_name, NAME_SIZE))
- name_recvd = 1;
- Throttle(10, &timer);
- }
- if(!name_recvd)
- printf("No name entered within timeout, will use default: %s\n",
- DEFAULT_SERVER_NAME);
-
- /* If no nickname received, use default: */
- if(strlen(server_name) == 0)
- strncpy(server_name, DEFAULT_SERVER_NAME, NAME_SIZE);
-#else
- /* HACK - until we figure out how to do nonblocking stdin
- * in Windows, we just stick in the default name:
- */
- strncpy(server_name, DEFAULT_SERVER_NAME, NAME_SIZE);
-#endif
- }
-
-
- // Zero out our client list:
- {
- int i = 0;
- for(i = 0; i < MAX_CLIENTS; i++)
- {
- client[i].game_ready = 0; /* waiting for user to OK game start */
- client[i].name[0] = '\0'; /* no nicknames yet */
- client[i].sock = NULL; /* sockets start out unconnected */
- client[i].score = 0;
- }
- }
-
-
- //Now open a UDP socket to listen for clients broadcasting to find the server:
- udpsock = SDLNet_UDP_Open(DEFAULT_PORT);
- if(!udpsock)
- {
- printf("SDLNet_UDP_Open: %s\n", SDLNet_GetError());
- return 0;
- }
-
- // Indicates success:
- return 1;
-}
-
-
-
-//Free resources, closing sockets, call MC_EndGame(), and so forth:
-void cleanup_server(void)
-{
- int i;
- /* Close the client socket(s) */
-
- for(i = 0; i < MAX_CLIENTS; i++)
- {
- if(client[i].sock != NULL)
- {
- SDLNet_TCP_Close(client[i].sock); //close all the client sockets one by one
- client[i].sock = NULL; // So we don't segfault in case cleanup()
- } // somehow gets called more than once.
- }
-
- if (client_set != NULL)
- {
- SDLNet_FreeSocketSet(client_set); //releasing the memory of the client socket set
- client_set = NULL; //this helps us remember that this set is not allocated
- }
-
- if(server_sock != NULL)
- {
- SDLNet_TCP_Close(server_sock);
- server_sock = NULL;
- }
-
- SDLNet_Quit();
-
- /* Clean up mathcards heap memory */
- MC_EndGame();
-}
-
-
-/* Handle any arguments passed from command line */
-void server_handle_command_args(int argc, char* argv[])
-{
- int i;
-
- for (i = 1; i < argc; i++)
- {
- if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)
- {
- /* Display help message: */
- printf("\n");
- cleanup_server();
- exit(0);
- }
- else if (strcmp(argv[i], "--debug-lan") == 0)
- {
- debug_status |= debug_lan;
- }
-
- else if (strcmp(argv[i], "--copyright") == 0 ||
- strcmp(argv[i], "-c") == 0)
- {
- printf(
- "\n\"Tux, of Math Command Server\" version " VERSION ", Copyright (C) 2009,\n"
- "David Bruce, Akash Gangil, and the Tux4Kids Project.\n"
- "This program is free software; you can redistribute it and/or\n"
- "modify it under the terms of the GNU General Public License\n"
- "as published by the Free Software Foundation. See COPYING.txt\n"
- "\n"
- "This program is distributed in the hope that it will be useful,\n"
- "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
- "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
- "\n");
-
- cleanup_server();
- exit(0);
- }
- else if (strcmp(argv[i], "--usage") == 0 ||
- strcmp(argv[i], "-u") == 0)
- {
- /* Display (happy) usage: */
-
-// usage(0, argv[0]);
- }
- else if ((strcmp(argv[i], "--name") == 0 || strcmp(argv[i], "-n") == 0)
- && (i + 1 < argc))
- {
- strncpy(server_name, argv[i + 1], NAME_SIZE);
- need_server_name = 0;
- }
- }
-}
-
-
-// ----------- Top level functions in main loop ---------------:
-
-//check_UDP() is the server side of the client-server autodetection system.
-//When a client wants to connect, it sends a UDP broadcast to the local
-//network on this port, and the server sends a response.
-void check_UDP(void)
-{
- int recvd = 0;
- UDPpacket* in = SDLNet_AllocPacket(NET_BUF_LEN);
- recvd = SDLNet_UDP_Recv(udpsock, in);
-
- // See if packet contains identifying string:
- if(strncmp((char*)in->data, "TUXMATH_CLIENT", strlen("TUXMATH_CLIENT")) == 0)
- {
- UDPpacket* out;
- int sent = 0;
- char buf[NET_BUF_LEN];
- // Send "I am here" reply so client knows where to connect socket,
- // with configurable identifying string so user can distinguish
- // between multiple servers on same network (e.g. "Mrs. Adams' Class");
- out = SDLNet_AllocPacket(NET_BUF_LEN);
- snprintf(buf, NET_BUF_LEN, "%s\t%s", "TUXMATH_SERVER", server_name);
- snprintf(out->data, NET_BUF_LEN, "%s", buf);
- out->len = strlen(buf) + 1;
- out->address.host = in->address.host;
- out->address.port = in->address.port;
-
- sent = SDLNet_UDP_Send(udpsock, -1, out);
-
- SDLNet_FreePacket(out);
- }
-}
-
-
-
-
-//update_clients() sees if anyone is trying to connect, and connects if a slot
-//is open and the game is not in progress. The purpose is to make sure our
-//client set accurately reflects the current state.
-void update_clients(void)
-{
- TCPsocket temp_sock = NULL; /* Just used when client can't be accepted */
- int slot = 0;
- int sockets_used = 0;
- char buffer[NET_BUF_LEN];
-
-
- /* See if we have a pending connection: */
- temp_sock = SDLNet_TCP_Accept(server_sock);
- if (!temp_sock) /* No one waiting to join - do nothing */
- {
- return; // Leave num_clients unchanged
- }
-
- // See if any slots are available:
- slot = find_vacant_client();
- if (slot == -1) /* No vacancies: */
- {
- snprintf(buffer, NET_BUF_LEN,
- "%s\t%s",
- "PLAYER_MSG",
- "Sorry, already have maximum number of clients connected");
- SDLNet_TCP_Send(temp_sock, buffer, NET_BUF_LEN);
- //hang up:
- SDLNet_TCP_Close(temp_sock);
- temp_sock = NULL;
-
- DEBUGMSG(debug_lan, "update_clients() - no vacant slot found\n");
-
- return; // Leave num_clients unchanged
- }
-
- //If everyone is disconnected, game no longer in progress:
- check_game_clients();
-
- // If game already started, send our regrets:
- if(game_in_progress)
- {
- snprintf(buffer, NET_BUF_LEN,
- "%s",
- "GAME_IN_PROGRESS");
- SDLNet_TCP_Send(temp_sock, buffer, NET_BUF_LEN);
- //hang up:
- SDLNet_TCP_Close(temp_sock);
- temp_sock = NULL;
-
- DEBUGMSG(debug_lan, "update_clients() - game already started\n");
-
- return; // Leave num_clients unchanged
- }
-
- // If we get to here, we have room for the new connection and the
- // game is not in progress, so we connect:
- DEBUGMSG(debug_lan, "creating connection for client[%d].sock:\n", slot);
-
- client[slot].sock = temp_sock;
-
- /* Add client socket to set: */
- sockets_used = SDLNet_TCP_AddSocket(client_set, client[slot].sock);
- if(sockets_used == -1) //No way this should happen
- {
- printf("SDLNet_AddSocket: %s\n", SDLNet_GetError());
- cleanup_server();
- exit(EXIT_FAILURE);
- }
-
- /* At this point num_clients can be updated: */
- num_clients = sockets_used;
-
- /* Now we can communicate with the client using client[i].sock socket */
- /* serv_sock will remain opened waiting other connections */
-
-
- /* Get the remote address */
- DEBUGCODE(debug_lan)
- {
- IPaddress* client_ip = NULL;
- client_ip = SDLNet_TCP_GetPeerAddress(client[slot].sock);
-
- printf("num_clients = %d\n", num_clients);
- if (client_ip != NULL)
- /* Print the address, converting in the host format */
- {
- printf("Client connected\n>\n");
- printf("Client: IP = %x, Port = %d\n",
- SDLNet_Read32(&client_ip->host),
- SDLNet_Read16(&client_ip->port));
- }
- else
- fprintf(stderr, "SDLNet_TCP_GetPeerAddress: %s\n", SDLNet_GetError());
- }
-
- return;
-}
-
-
-
-// check_messages() is where we look at the client socket set to see which
-// have sent us messages. This function is used in each server loop whether
-// or not a math game is in progress (although we expect different messages
-// during a game from those encountered outside of a game)
-
-int server_check_messages(void)
-{
- int actives = 0, i = 0;
- int ready_found = 0;
- char buffer[NET_BUF_LEN];
-
-
- /* Check the client socket set for activity: */
- actives = SDLNet_CheckSockets(client_set, 0);
-// printf("in check_messages(), actives = %d\n", actives);
- if(actives == -1)
- {
- printf("SDLNet_CheckSockets: %s\n", SDLNet_GetError());
- //most of the time this is a system error, where perror might help you.
- perror("SDLNet_CheckSockets");
- }
-
- else if(actives)
- {
- DEBUGMSG(debug_lan, "There are %d sockets with activity\n", actives);
-
- // check all sockets with SDLNet_SocketReady and handle the active ones.
- // NOTE we have to check all the slots in the set because
- // the set will become discontinuous if someone disconnects
- // NOTE this will only pick up the first message for each socket each time
- // check_messages() called - probably OK if we just get it next time through.
- for(i = 0; i < MAX_CLIENTS; i++)
- {
- if((client[i].sock != NULL)
- && (SDLNet_SocketReady(client[i].sock)))
- {
- ready_found++;
-
- DEBUGMSG(debug_lan, "client socket %d is ready\n", i);
-
- if (SDLNet_TCP_Recv(client[i].sock, buffer, NET_BUF_LEN) > 0)
- {
- DEBUGMSG(debug_lan, "buffer received from client %d is: %s\n", i, buffer);
-
- /* Here we pass the client number and the message buffer */
- /* to a suitable function for further action: */
- if(game_in_progress)
- {
- handle_client_game_msg(i, buffer);
- }
- else
- {
- handle_client_nongame_msg(i, buffer);
- }
- // See if game is ended because everyone has left:
- check_game_clients();
- }
- else // Socket activity but cannot receive - client invalid
- {
- printf("Client %d active but receive failed - apparently disconnected\n>\n", i);
- remove_client(i);
- }
- }
- } // end of for() loop - all client sockets checked
- // Make sure all the active sockets reported by SDLNet_CheckSockets()
- // are accounted for:
-
- if(actives > ready_found)
- {
- printf("Warning: SDLNet_CheckSockets() reported %d active sockets,\n"
- "but only %d detected by SDLNet_SocketReady()\n", actives, ready_found);
- //Presently, this just runs ping_client() on all the sockets:
- //test_connections();
- }
- }
- return 1;
-}
-
-
-
-
-// client management utilities:
-
-//Returns the index of the first vacant client, or -1 if all clients full
-int find_vacant_client(void)
-{
- int i = 0;
- while (client[i].sock && i < MAX_CLIENTS)
- i++;
- if (i == MAX_CLIENTS)
- {
- fprintf(stderr, "All clients checked, none vacant\n");
- i = -1;
- }
- return i;
-}
-
-
-void remove_client(int i)
-{
- printf("Removing client[%d] - name: %s\n>\n", i, client[i].name);
-
- SDLNet_TCP_DelSocket(client_set, client[i].sock);
-
- if(client[i].sock != NULL)
- SDLNet_TCP_Close(client[i].sock);
-
- client[i].sock = NULL;
- client[i].game_ready = 0;
- client[i].name[0] = '\0';
-}
-
-
-// check_game_clients() reviews the game_ready flags of all the connected
-// clients to determine if a new game is started, or if an old game needs
-// to be ended because all the players have left. If it finds both "playing"
-// and "nonplaying clients", it leaves game_in_progress unchanged.
-
-// TODO this is not very sophisticated, and only supports one game at a time.
-// We may want to make this extensible to multiple simultaneous games, perhaps
-// with each game in its own thread with its own socket set and mathcards instance.
-// FIXME we need to do more than just toggle game_in_progress - should have
-// start_game() and end_game() functions that make sure mathcards is
-// properly set up or cleaned up.
-void check_game_clients(void)
-{
- int i = 0;
-
- //If the game is already started, we leave it running as long as at least
- //one client is both connected and willing to play:
- if(game_in_progress)
- {
- int someone_still_playing = 0;
- for(i = 0; i < MAX_CLIENTS; i++)
- {
- if((client[i].sock != NULL)
- && client[i].game_ready)
- {
- someone_still_playing = 1;
- break;
- }
- }
-
- if(!someone_still_playing)
- {
- printf("All the clients have left the game, setting game_in_progress = 0.\n");
- game_in_progress = 0;
- }
- }
- //If the game hasn't started yet, we only start it
- //if all connected clients are ready:
- //FIXME should add a timeout so the game eventually starts without
- //those who don't answer
- else
- {
- int someone_connected = 0;
- int someone_not_ready = 0;
- for(i = 0; i < MAX_CLIENTS; i++)
- {
- if(client[i].sock != NULL)
- {
- someone_connected = 1;
- if (!client[i].game_ready)
- {
- someone_not_ready = 1;
- }
- }
- }
- if(someone_connected && !someone_not_ready)
- start_game();
- }
-}
-
-
-
-void handle_client_nongame_msg(int i, char* buffer)
-{
- char buf[NET_BUF_LEN];
-
- if(strncmp(buffer, "START_GAME", strlen("START_GAME")) == 0)
- {
- snprintf(buf, NET_BUF_LEN,
- "Player %s ready to start math game",
- client[i].name);
- broadcast_msg(buf);
- client[i].game_ready = 1;
- //This will call start_game() if all the other clients are ready:
- check_game_clients();
- }
- else if(strncmp(buffer, "SET_NAME", strlen("SET_NAME")) == 0)
- {
- msg_set_name(i, buffer);
- }
-}
-
-
-int handle_client_game_msg(int i , char* buffer)
-{
- DEBUGMSG(debug_lan, "Buffer received from client: %s\n", buffer);
-
- if(strncmp(buffer, "CORRECT_ANSWER", strlen("CORRECT_ANSWER")) == 0)
- {
- game_msg_correct_answer(i, buffer);
- }
-
- else if(strncmp(buffer, "WRONG_ANSWER",strlen("WRONG_ANSWER")) == 0) /* Player answered the question incorrectly , meaning comet crashed into a city or an igloo */
- {
- game_msg_wrong_answer(i, buffer);
- }
-
- else if(strncmp(buffer, "LEAVE_GAME", strlen("LEAVE_GAME")) == 0)
- {
- client[i].game_ready = 0; /* Player quitting game but not disconnecting */
- }
-
- else if(strncmp(buffer, "exit",strlen("exit")) == 0) /* Terminate this connection */
- {
- game_msg_exit(i);
- }
-
- else if(strncmp(buffer, "quit",strlen("quit")) == 0) /* Quit the program */
- {
- game_msg_quit(i);
- return(1);
- }
- else
- {
- printf("command %s not recognized\n", buffer);
- }
- return(0);
-}
-
-
-
-int msg_set_name(int i, char* buf)
-{
- char* p;
-
- if(buf == NULL)
- return 0;
-
- p = strchr(buf, '\t');
- if(p)
- {
- p++;
- strncpy(client[i].name, p, NAME_SIZE);
- return 1;
- }
- else
- return 0;
-}
-
-
-
-void game_msg_correct_answer(int i, char* inbuf)
-{
- char outbuf[NET_BUF_LEN];
- char* p = NULL;
- int id = -1;
- float t = -1;
- int points = 0;
-
- if(!inbuf)
- return;
-
- //parse inbuf to get question id:
- p = strchr(inbuf, '\t');
- if(!p)
- return;
- p++;
- id = atoi(p);
- //Now get time player took to answer:
- p = strchr(p, '\t');
- if(!p)
- t = -1;
- else
- {
- p++;
- t = atof(p);
- }
-
- //Tell mathcards so lists get updated:
- points = MC_AnsweredCorrectly(id, t);
- if(!points)
- return;
- //If we get to here, the id was successfully parsed out of inbuf
- //and the corresponding question was found.
- client[i].score += points;
-
- //Announcement for server and all clients:
- snprintf(outbuf, NET_BUF_LEN,
- "question id %d was answered in %f seconds for %d points by %s",
- id, t, points, client[i].name);
- broadcast_msg(outbuf);
-
- DEBUGMSG(debug_lan, "game_msg_correct_answer(): %s\n", outbuf);
-
- //Tell all players to remove that question:
- remove_question(id);
- //send the next question to everyone:
- game_msg_next_question();
- //and update the game counters:
- send_counter_updates();
- //and the scores:
- send_score_updates();
-}
-
-
-void game_msg_wrong_answer(int i, char* inbuf)
-{
- char outbuf[NET_BUF_LEN];
- char* p;
- int id;
-
- if(!inbuf)
- return;
-
- //parse inbuf to get question id:
- p = strchr(inbuf, '\t');
- if(!p)
- return;
- p++;
- id = atoi(p);
-
- //Tell mathcards so lists get updated:
- if(!MC_NotAnsweredCorrectly(id))
- return;
- //If we get to here, the id was successfully parsed out of inbuf
- //and the corresponding question was found.
-
- //Announcement for server and all clients:
- snprintf(outbuf, NET_BUF_LEN,
- "question id %d was missed by %s\n",
- id, client[i].name);
- broadcast_msg(outbuf);
- //Tell all players to remove that question:
- remove_question(id);
- //send the next question to everyone:
- game_msg_next_question();
- //and update the game counters:
- send_counter_updates();
-}
-
-
-
-void game_msg_next_question(void)
-{
- MC_FlashCard flash;
-
- /* Get next question from MathCards: */
- if (!MC_NextQuestion(&flash))
- {
- /* no more questions available */
- printf("MC_NextQuestion() returned NULL - no questions available\n");
- return;
- }
-
- DEBUGMSG(debug_lan, "In game_msg_next_question(), about to send:\n");
- DEBUGCODE(debug_lan) print_card(flash);
-
- /* Send it to all the clients: */
- add_question(&flash);
-}
-
-
-
-
-
-void game_msg_exit(int i)
-{
- printf("LEFT the GAME : %s",client[i].name);
- remove_client(i);
-}
-
-
-
-//FIXME don't think we want to allow players to shut down the server
-void game_msg_quit(int i)
-{
- printf("Server has been shut down by %s\n", client[i].name);
- cleanup_server();
- exit(9); // '9' means exit ;) (just taken an arbitary no:)
-}
-
-
-/* Now this gets called to actually start the game once all the players */
-/* have indicated that they are ready: */
-void start_game(void)
-{
- char buf[NET_BUF_LEN];
- int j;
-
-
- /* NOTE this should no longer be needed - doing the same thing earlier */
- /*This loop sees that the game starts only when all the players are ready */
- /* i.e. if someone is connected but not ready, we return. */
- for(j = 0; j < MAX_CLIENTS; j++)
- {
- // Only check sockets that aren't null:
- if((client[j].game_ready != 1)
- && (client[j].sock != NULL))
- {
- printf("Warning - start_game() entered when someone not ready\n");
- return;
- }
- }
-
-
- /***********************Will be modified**************/
- //Tell everyone we are starting and count who's really in:
- num_clients = 0;
- snprintf(buf, NET_BUF_LEN,
- "%s\n",
- "GO_TO_GAME");
- for(j = 0; j < MAX_CLIENTS; j++)
- {
- if((client[j].game_ready == 1)
- && (client[j].sock != NULL))
- {
- if(SDLNet_TCP_Send(client[j].sock, buf, NET_BUF_LEN) == NET_BUF_LEN)
- num_clients++;
- else
- {
- printf("in start_game() - failed to send to client %d, removing\n", j);
- remove_client(j);
- }
- }
- }
- /*****************************************************/
-
-
- /* If no players join the game (should not happen) */
- if(num_clients == 0)
- {
- printf("There were no players........=(\n");
- return;
- }
-
- DEBUGMSG(debug_lan, "We have %d players.......\n", num_clients);
-
- game_in_progress = 1; //setting the game_in_progress flag to '1'
- //Start a new math game as far as mathcards is concerned:
- if (!MC_StartGame())
- {
- fprintf(stderr, "\nMC_StartGame() failed!");
- return;
- }
-
- game_in_progress = 1;
-
- // Zero out scores:
- for(j = 0; j < MAX_CLIENTS; j++)
- client[j].score = 0;
-
- /* Send enough questions to fill the initial comet slots (currently 10) */
- for(j = 0; j < QUEST_QUEUE_SIZE; j++)
- {
- if (!MC_NextQuestion(&flash))
- {
- /* no more questions available */
- printf("MC_NextQuestion() returned NULL - no questions available\n");
- return;
- }
-
- DEBUGMSG(debug_lan, "In start_game(), about to send:\n");
- DEBUGCODE(debug_lan) print_card(flash);
-
- //Send to all clients with add_question();
- add_question(&flash);
- }
- //Send all the clients the counter totals:
- send_counter_updates();
- send_score_updates();
-}
-
-
-
-//More centralized function to update the clients of the number of
-//questions remaining, whether the mission has been accomplished,
-//and so forth:
-int send_counter_updates(void)
-{
- int total_questions;
-
- //If game won, tell everyone:
- if(MC_MissionAccomplished())
- {
- char buf[NET_BUF_LEN];
- snprintf(buf, NET_BUF_LEN, "%s", "MISSION_ACCOMPLISHED");
- transmit_all(buf);
- }
-
- //Tell everyone how many questions left:
- total_questions = MC_TotalQuestionsLeft();
- {
- char buf[NET_BUF_LEN];
- snprintf(buf, NET_BUF_LEN, "%s\t%d", "TOTAL_QUESTIONS", total_questions);
- transmit_all(buf);
- }
- return 1;
-}
-
-
-int send_score_updates(void)
-{
- int i = 0;
-
- /* Count how many players are active and send number to clients: */
- {
- int connected_players = 0;
- char buf[NET_BUF_LEN];
- for(i = 0; i < MAX_CLIENTS; i++)
- if((client[i].game_ready == 1) && (client[i].sock != NULL))
- connected_players++;
-
- snprintf(buf, NET_BUF_LEN, "%s\t%d", "CONNECTED_PLAYERS",
- connected_players);
- transmit_all(buf);
- }
-
- /* Now send out all the names and scores: */
- for(i = 0; i < MAX_CLIENTS; i++)
- {
- if((client[i].game_ready == 1)
- && (client[i].sock != NULL))
- {
- char buf[NET_BUF_LEN];
- snprintf(buf, NET_BUF_LEN, "%s\t%d\t%s\t%d", "UPDATE_SCORE",
- i,
- client[i].name,
- client[i].score);
- transmit_all(buf);
- }
- }
-
- return 1;
-}
-
-
-/* Sends a new question to all clients: */
-int add_question(MC_FlashCard* fc)
-{
- char buf[NET_BUF_LEN];
-
- if(!fc)
- return 0;
-
- snprintf(buf, NET_BUF_LEN,"%s\t%d\t%d\t%d\t%s\t%s\n",
- "ADD_QUESTION",
- fc->question_id,
- fc->difficulty,
- fc->answer,
- fc->answer_string,
- fc->formula_string);
- transmit_all(buf);
- return 1;
-}
-
-/* Tells all clients to remove a specific question: */
-int remove_question(int id)
-{
- char buf[NET_BUF_LEN];
- snprintf(buf, NET_BUF_LEN, "%s\t%d", "REMOVE_QUESTION", id);
- transmit_all(buf);
- return 1;
-}
-
-
-/* Sends a string for the client to display to player: */
-int player_msg(int i, char* msg)
-{
- char buf[NET_BUF_LEN];
- if(!msg)
- {
- DEBUGMSG(debug_lan, "player_msg() - msg argument is NULL\n");
- return 0;
- }
-
- /* Add header: */
- snprintf(buf, NET_BUF_LEN, "%s\t%s", "PLAYER_MSG", msg);
- //NOTE transmit() validates index and socket
- return transmit(i, buf);
-}
-
-/* Send a player message to all clients: */
-void broadcast_msg(char* msg)
-{
- int i = 0;
- if (!msg)
- return;
- for(i = 0; i < MAX_CLIENTS; i++)
- player_msg(i, msg);
-}
-
-/* Send string to client. String should already have its header */
-int transmit(int i, char* msg)
-{
- char buf[NET_BUF_LEN];
-
- //Validate arguments;
- if(i < 0 || i > MAX_CLIENTS)
- {
- DEBUGMSG(debug_lan,"transmit() - invalid index argument\n");
- return 0;
- }
-
- if(!msg)
- {
- DEBUGMSG(debug_lan, "transmit() - msg argument is NULL\n");
- return 0;
- }
-
- if(!client[i].sock)
- {
- return 0;
- }
-
- //NOTE SDLNet's Send() keeps sending until the requested length is
- //sent, so it really is an error if we send less thatn NET_BUF_LEN
- snprintf(buf, NET_BUF_LEN, "%s", msg);
- if(SDLNet_TCP_Send(client[i].sock, buf, NET_BUF_LEN) < NET_BUF_LEN)
- {
- printf("The client %s is disconnected\n", client[i].name);
- remove_client(i);
- return 0;
- }
- //Success:
- return 1;
-}
-
-
-/* Send the message to all clients: */
-int transmit_all(char* msg)
-{
- int i = 0;
- if (!msg)
- return 0;
-
- for(i = 0; i < MAX_CLIENTS; i++)
- transmit(i, msg);
-
- return 1;
-}
-
-
-
-//Here we read up to max_length bytes from stdin into the buffer.
-//The first '\n' in the buffer, if present, is replaced with a
-//null terminator.
-//returns 0 if no data ready, 1 if at least one byte read.
-//NOTE for this to work we must first set stdin to O_NONBLOCK with:
-// fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK);
-
-int read_stdin_nonblock(char* buf, size_t max_length)
-{
- int bytes_read = 0;
- char* term = NULL;
- buf[0] = '\0';
-
- bytes_read = fread (buf, 1, max_length, stdin);
- term = strchr(buf, '\n');
- if (term)
- *term = '\0';
-
- if(bytes_read > 0)
- bytes_read = 1;
- else
- bytes_read = 0;
-
- return bytes_read;
-}
-
-
-
-int ServerRunning(void)
-{
- return server_running;
-}
-
-#endif
diff --git a/src/server.h b/src/server.h
deleted file mode 100644
index bec8163..0000000
--- a/src/server.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-
- server.h
-
- Author: David Bruce, Akash Gangil and the TuxMath team, (C) 2009
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-
-#ifndef TM_SERVER_H
-#define TM_SERVER_H
-
-#include "config.h"
-
-#ifdef HAVE_LIBSDL_NET
-
-#include "SDL_net.h"
-
-#define NAME_SIZE 50
-#define DEFAULT_SERVER_NAME "TuxMath LAN Server"
-#define SERVER_NAME_TIMEOUT 30000
-
-typedef struct client_type {
- int game_ready; //game_ready = 1 means client has said OK to start
- char name[NAME_SIZE];
- int score;
- TCPsocket sock;
-}client_type;
-
-
-
-/*enum for commands coming from the client side*/
-// enum {
-// EXIT,
-// QUIT,
-// CORRECT_ANSWER,
-// NOT_ANSWERED_CORRECTLY,
-// NEXT_QUESTION,
-// TOTAL_QUESTIONS_LEFT
-// };
-
-
-// /*enum for messages for SendMessage*/
-// enum {
-// ANSWER_CORRECT,
-// LIST_SET_UP,
-// NO_QUESTION_LIST
-// };
-
-
-/* Ways to run the server - all accept command-line style arguments: */
-
-/* 1. Type "tuxmathserver" at command line to run as standalone program. */
-
-/* From within Tuxmath: */
-
-#ifdef HAVE_PTHREAD_H
-/* 2. Using POSIX threads library (RECOMMENDED if pthreads available on your system): */
-int RunServer_pthread(int argc, char* argv[]);
-#endif
-
-/* 3. As a standalone program using system() - same as "tuxmathserver" at console: */
-int RunServer_prog(int argc, char* argv[]);
-
-/* TODO 4. Using old-school Unix fork() call: */
-int RunServer_fork(int argc, char* argv[]);
-
-/* 2, 3, and 4 all return immediately, with the server running in a separate thread or process. But if you don't mind waiting... */
-/* 5. Plain "blocking" function call, leaving scheduling issues up to you: */
-int RunServer(int argc, char **argv);
-
-/* Find out if server is already running: */
-int ServerRunning(void);
-#endif
-
-#endif
diff --git a/src/servermain.c b/src/servermain.c
deleted file mode 100644
index f508f11..0000000
--- a/src/servermain.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-* C Implementation: servermain.c
-*
-* Description: main() function to allow standalone use of server program for
-* LAN-based play in Tux,of Math Command.
-*
-*
-* Author: David Bruce and the TuxMath team, (C) 2009
-* Developers list: <tuxmath-devel at lists.sourceforge.net>
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*
-*/
-
-#include "server.h"
-
-/* This function has to be in its own file that is not linked into tuxmath */
-/* itself because there can only be one main() in a program. All of the */
-/* server functionality is contained in server.h and server.c */
-int main(int argc, char** argv)
-{
-#ifdef HAVE_LIBSDL_NET
- return RunServer(argc, argv);
-#else
- return 0;
-#endif
-}
diff --git a/src/testclient.c b/src/testclient.c
deleted file mode 100644
index 7618621..0000000
--- a/src/testclient.c
+++ /dev/null
@@ -1,544 +0,0 @@
-/*
-* C Implementation: server.c
-*
-* Description: Test client program for LAN-based play in Tux,of Math Command.
-*
-*
-* Author: Akash Gangil, David Bruce, and the TuxMath team, (C) 2009
-* Developers list: <tuxmath-devel at lists.sourceforge.net>
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*
-* NOTE: This file was initially based on example code from The Game Programming Wiki
-* (http://gpwiki.org), in a tutorial covered by the GNU Free Documentation License 1.2.
-* No invariant sections were indicated, and no separate license for the example code
-* was listed. The author was also not listed. AFAICT,this scenario allows incorporation of
-* derivative works into a GPLv2+ project like TuxMath - David Bruce
-*/
-#ifdef HAVE_LIBSDL_NET
-
-#include "globals.h"
-#include "transtruct.h"
-#include "mathcards.h"
-#include "testclient.h"
-#include "throttle.h"
-#include "network.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-//#include "SDL_net.h"
-
-/* Local (to testclient.c) "globals": */
-
-int quit = 0;
-int game_status = GAME_NOT_STARTED;
-
-MC_FlashCard comets[QUEST_QUEUE_SIZE]; //current questions
-int remaining_quests = 0;
-
-
-/* Local function prototypes: */
-int playgame(void);
-int erase_flashcard(MC_FlashCard* fc);
-MC_FlashCard* find_comet_by_id(int id);
-MC_FlashCard* check_answer(int ans);
-
-int read_stdin_nonblock(char* buf, size_t max_length);
-
-/* Functions to handle messages from server: */
-int game_check_msgs(void);
-int add_quest_recvd(char* buf);
-int remove_quest_recvd(char* buf);
-int player_msg_recvd(char* buf);
-int total_quests_recvd(char* buf);
-int mission_accompl_recvd(char* buf);
-
-/* Display to player: */
-void print_current_quests(void);
-
-/* Main function: ------------------------------------- */
-
-int main(int argc, char **argv)
-{
- char buffer[NET_BUF_LEN]; // for command-line input
- int servers_found = 0;
- int server_number = -1;
- Uint32 timer = 0;
-
- //Scan local network to find running server:
- servers_found = LAN_DetectServers();
-
- if(servers_found < 1)
- {
- printf("No server could be found - exiting.\n");
- exit(EXIT_FAILURE);
- }
- else if(servers_found == 1) //One server - connect without player intervention
- {
- printf("Single server found - connecting automatically...");
-
- if(!LAN_AutoSetup(0)) //i.e.first (and only) entry in list
- {
- printf("setup_client() failed - exiting.\n");
- exit(EXIT_FAILURE);
- }
-
- printf("connected\n");
- }
-
-
- else // More than one server - will have to get player selection:
- {
- while(server_number < 0 || server_number >= servers_found)
- {
- printf("The following TuxMath servers were detected:\n");
- print_server_list();
- printf("Enter the SERVER NUMBER you would like to connect to:\n");
- scanf("%d", &server_number);
- if(server_number < 0 || server_number >= servers_found)
- printf("Illegal value - try again.\n");
- }
- if(!LAN_AutoSetup(server_number)) //i.e.first (and only) entry in list
- {
- printf("setup_client() failed - exiting.\n");
- exit(EXIT_FAILURE);
- }
-
- printf("connected\n");
- }
-
-
- /* Now we are connected - get nickname from player: */
- {
- char name[NAME_SIZE];
- char* p;
-
- printf("Please enter your name:\n>\n");
- fgets(buffer, NAME_SIZE, stdin);
- p = strchr(buffer, '\n'); //get rid of newline character
- if(p)
- *p = '\0';
- strncpy(name, buffer, NAME_SIZE);
- /* If no nickname received, use default: */
- if(strlen(name) == 1)
- strcpy(name, "Anonymous Coward");
-
- snprintf(buffer, NET_BUF_LEN, "%s", name);
- LAN_SetName(name);
- }
-
- printf("Welcome to the Tux Math Test Client!\n");
- printf("Type:\n"
- "'game' to start math game;\n"
- "'exit' to end client leaving server running;\n"
- "'quit' to end both client and server\n>\n");
-
-
- /* Set stdin to be non-blocking: */
- fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK);
-
-
- quit = 0;
- while(!quit)
- {
- // See if we have any messages from server:
- game_check_msgs();
-
- //Get user input from command line and send it to server:
- /*now display the options*/
- if(read_stdin_nonblock(buffer, NET_BUF_LEN))
- {
- //Figure out if we are trying to quit:
- if( (strncmp(buffer, "exit", 4) == 0)
- ||(strncmp(buffer, "quit", 4) == 0))
-
- {
- quit = 1;
- }
- else if (strncmp(buffer, "game", 4) == 0)
- {
- // Begin the actual math game
- playgame();
- printf("Math game finished.\n\n");
- printf("Type:\n"
- "'game' to start math game;\n"
- "'exit' to end client leaving server running;\n"
- "'quit' to end both client and server\n>\n");
- }
- else
- {
- printf("Command not recognized. Type:\n"
- "'game' to start math game;\n"
- "'exit' to end client leaving server running;\n"
- "'quit' to end both client and server\n\n>\n");
- }
- }
- //Limit loop to once per 10 msec so we don't eat all CPU
- Throttle(10, &timer);
- }
-
- LAN_Cleanup();
-
- return EXIT_SUCCESS;
-}
-
-
-
-
-
-int game_check_msgs(void)
-{
- char buf[NET_BUF_LEN];
- int status = 1;
- while(1)
- {
- buf[0] = '\0';
- status = LAN_NextMsg(buf);
- if (status == -1) //Fatal error
- {
- printf("Error - LAN_NextMsg() returned -1\n");
- return -1;
- }
-
- if (status == 0) //No more messages
- {
- break;
- }
-
- DEBUGMSG(debug_lan, "Buffer from server is: %s\n", buf);
-
- /* Now we process the buffer according to the command: */
- if(strncmp(buf, "SEND_QUESTION", strlen("SEND_QUESTION")) == 0)
- {
- if(!add_quest_recvd(buf))
- printf("SEND_QUESTION received but could not add question\n");
- else
- // If we successfully added question, show new questions to user:
- print_current_quests();
- }
- else if(strncmp(buf, "ADD_QUESTION", strlen("ADD_QUESTION")) == 0)
- {
- if(!add_quest_recvd(buf))
- printf("ADD_QUESTION received but could not add question\n");
- else
- print_current_quests();
- }
- else if(strncmp(buf, "REMOVE_QUESTION", strlen("REMOVE_QUESTION")) == 0)
- {
- if(!remove_quest_recvd(buf)) //remove the question with id in buf
- printf("REMOVE_QUESTION received but could not remove question\n");
- else
- print_current_quests();
- }
- else if(strncmp(buf, "SEND_MESSAGE", strlen("SEND_MESSAGE")) == 0)
- {
- printf("%s\n", buf);
- }
- else if(strncmp(buf, "PLAYER_MSG", strlen("PLAYER_MSG")) == 0)
- {
- player_msg_recvd(buf);
- }
- else if(strncmp(buf, "TOTAL_QUESTIONS", strlen("TOTAL_QUESTIONS")) == 0)
- {
- //update the "questions remaining" counter
- total_quests_recvd(buf);
- }
- else if(strncmp(buf, "MISSION_ACCOMPLISHED", strlen("MISSION_ACCOMPLISHED")) == 0)
- {
- game_status = GAME_OVER_WON;
- }
- else
- {
- printf("game_check_msgs() - unrecognized message: %s\n", buf);
- }
- }
-
- return 1;
-}
-
-
-
-int add_quest_recvd(char* buf)
-{
- MC_FlashCard* fc = find_comet_by_id(-1);
-
- if(!fc || !buf)
- {
- printf("NULL fc or buf\n");
- return 0;
- }
- /* function call to parse buffer and receive question */
- if(!Make_Flashcard(buf, fc))
- {
- printf("Unable to parse buffer into FlashCard\n");
- return 0;
- }
-
- return 1;
-}
-
-
-
-int remove_quest_recvd(char* buf)
-{
- int id = 0;
- char* p = NULL;
- MC_FlashCard* fc = NULL;
-
- if(!buf)
- return 0;
-
- p = strchr(buf, '\t');
- if(!p)
- return 0;
-
- id = atoi(p);
- fc = find_comet_by_id(id);
- if(!fc)
- return 0;
-
- erase_flashcard(fc);
- return 1;
-}
-
-
-
-/* This function prints the 'msg' part of the buffer (i.e. everything */
-/* after the first '\t') to stdout. */
-int player_msg_recvd(char* buf)
-{
- char* p;
- if(buf == NULL)
- return 0;
- p = strchr(buf, '\t');
- if(p)
- {
- p++;
- printf("%s\n", p);
- return 1;
- }
- else
- return 0;
-}
-
-
-int total_quests_recvd(char* buf)
-{
- char* p;
- if(buf == NULL)
- return 0;
- p = strchr(buf, '\t');
- if(p)
- {
- p++;
- remaining_quests = atoi(p);
- return 1;
- }
- else
- return 0;
-}
-
-
-
-
-
-
-int playgame(void)
-{
- int ans = 0;
- MC_FlashCard* fc = NULL;
- char buf[NET_BUF_LEN];
- Uint32 timer = 0;
-
- printf("\nStarting Tux, of the Math Command Line ;-)\n");
- printf("Waiting for other players to be ready...\n\n");
-
- //Tell server we're ready to start:
- LAN_StartGame();
- game_status = GAME_IN_PROGRESS;
-
- /* Start out with our "comets" empty: */
- {
- int i;
- for(i = 0; i < QUEST_QUEUE_SIZE; i ++)
- erase_flashcard(&comets[i]);
- }
-
- //Begin game loop:
- while (game_status == GAME_IN_PROGRESS)
- {
-
- //Check our network messages, bailing out for fatal errors:
- if (game_check_msgs() == -1)
- return -1;
-
-
- //Now we check for any user responses
-
- //This function returns 1 and updates buf with input from
- //stdin if input is present.
- //If no input, it returns 0 without blocking or waiting
- if(read_stdin_nonblock(buf, NET_BUF_LEN))
- {
- //While in game, these just quit the current math game:
- if ((strncmp(buf, "quit", 4) == 0)
- ||(strncmp(buf, "exit", 4) == 0)
- ||(strncmp(buf, "q", 1) == 0))
- {
- game_status = GAME_OVER_ESCAPE;
-// end = 1; //Exit our loop in playgame()
- //Tell server we are quitting current game:
- LAN_LeaveGame();
- }
- else
- {
- /*NOTE atoi() will return zero for any string that is not
- a valid int, not just '0' - should not be a big deal for
- our test program - DSB */
- ans = atoi(buf);
- fc = check_answer(ans);
- if((fc != NULL))
- {
- printf("%s is correct!\nAwait next question...\n>\n", buf);
- //Tell server we answered it right:
- //NOTE the '-1' means we aren't tracking times for testclient
- LAN_AnsweredCorrectly(fc->question_id, -1);
- erase_flashcard(fc);
- print_current_quests();
- }
- else //we got input, but not the correct answer:
- {
- int i = rand()%QUEST_QUEUE_SIZE;
- printf("Sorry, %s is incorrect. Try again!\n", buf);
- // Can't tell which question was the 'wrong' one, so we
- // a non-empty one at random. Note that this is just for
- // purposes of testing LAN_NotAnsweredCorrectly()
- while(-1 == comets[i].question_id)
- i = rand()%QUEST_QUEUE_SIZE;
- LAN_NotAnsweredCorrectly(comets[i].question_id);
- print_current_quests();
- }
- } //input wasn't any of our keywords
- } // Input was received
-
- Throttle(10, &timer); //so don't eat all CPU
- } //End of game loop
-
- switch(game_status)
- {
- case GAME_OVER_ESCAPE:
- printf("You quit :(\n");
- break;
- case GAME_OVER_WON:
- printf("You won! :-)\n");
- }
-
- DEBUGMSG(debug_lan, "Leaving playgame()\n");
-
- return 1;
-}
-
-
-
-
-//Here we read up to max_length bytes from stdin into the buffer.
-//The first '\n' in the buffer, if present, is replaced with a
-//null terminator.
-//returns 0 if no data ready, 1 if at least one byte read.
-//NOTE for this to work we must first set stdin to O_NONBLOCK with:
-// fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK);
-
-int read_stdin_nonblock(char* buf, size_t max_length)
-{
- int bytes_read = 0;
- char* term = NULL;
- buf[0] = '\0';
-
- bytes_read = fread (buf, 1, max_length, stdin);
- term = strchr(buf, '\n');
- if (term)
- *term = '\0';
-
- if(bytes_read > 0)
- bytes_read = 1;
- else
- bytes_read = 0;
-
- return bytes_read;
-}
-
-/* Display the current questions and the number of remaining questions: */
-void print_current_quests(void)
-{
- int i;
- printf("\n------------ Current Questions: -----------\n");
- for(i = 0; i < QUEST_QUEUE_SIZE; i ++)
- {
- if(comets[i].question_id != -1)
- printf("Comet %d - question %d:\t%s\n", i, comets[i].question_id, comets[i].formula_string);
- else
- printf("Comet %d:\tEmpty\n", i);
- }
- printf("-----------------------------------------------\n");
-}
-
-
-int erase_flashcard(MC_FlashCard* fc)
-{
- if(!fc)
- return 0;
- fc->formula_string[0] = '\0';
- fc->answer_string[0] = '\0';
- fc->question_id = -1;
- fc->answer = -9999;
- fc->difficulty = 0;
- return 1;
-}
-
-
-/* Return a pointer to an empty comet slot, */
-/* returning NULL if no vacancy found: */
-
-MC_FlashCard* find_comet_by_id(int id)
-{
- int i = 0;
- for(i = 0; i < QUEST_QUEUE_SIZE; i++)
- {
- if(comets[i].question_id == id)
- return &comets[i];
- }
- //if we don't find a match:
- return NULL;
-}
-
-/* Check the "comets" in order to see if the given */
-/* value matches the answer for any of the comets: */
-/* Returns a pointer to the matching comet, or */
-/* NULL if the answer doesn't match: */
-
-MC_FlashCard* check_answer(int ans)
-{
- int i = 0;
- for(i = 0; i < QUEST_QUEUE_SIZE; i++)
- {
- /* Make sure we don't "match" an empty question with a zero answer: */
- if( (comets[i].question_id != -1)
- && (comets[i].answer == ans))
-
- return &comets[i];
- }
- //if we don't find a matching question:
- return NULL;
-}
-#else
-/* if no SDL_net, do nothing: */
-int main(int argc, char **argv)
-{
- return 0;
-}
-#endif
diff --git a/src/testclient.h b/src/testclient.h
deleted file mode 100644
index 991be10..0000000
--- a/src/testclient.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-
- testclient.h
-
- Description: As of now it conatinsthe enum, which identifies
- the network commands , as they are added(WORK IN PROGRESS).
-
- Author: David Bruce ,Akash Gangil and the TuxMath team, (C) 2009
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-
-#ifndef TESTCLIENT_H
-#define TESTCLIENT_H
-
-
-enum {
- GAME_NOT_STARTED,
- GAME_IN_PROGRESS,
- GAME_OVER_WON,
- GAME_OVER_LOST,
- GAME_OVER_OTHER,
- GAME_OVER_ESCAPE,
- GAME_OVER_WINDOW_CLOSE,
- GAME_OVER_CHEATER,
- GAME_OVER_ERROR
-};
-
-
-enum {
- SEND_QUESTION
-};
-
-#endif
diff --git a/src/throttle.c b/src/throttle.c
deleted file mode 100644
index 5052fc8..0000000
--- a/src/throttle.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-* C Implementation: network.c
-*
-* Description: A simple function that uses SDL_Delay() to keep
-* loops from eating all available CPU.
-
-*
-* Author: David Bruce, and the TuxMath team, (C) 2009
-* Developers list: <tuxmath-devel at lists.sourceforge.net>
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*/
-
-
-#include "SDL.h"
-
-/* NOTE now store the time elsewhere to make function thread-safe */
-
-void Throttle(int loop_msec, Uint32* last_t)
-{
- Uint32 now_t, wait_t;
-
- if(!last_t)
- return;
-
- //Target loop time must be between 0 and 1000 msec:
- if(loop_msec < 0)
- loop_msec = 0;
- if(loop_msec > 1000)
- loop_msec = 1000;
-
- //See if we need to wait:
- now_t = SDL_GetTicks();
- if (now_t < (*last_t + loop_msec))
- {
- wait_t = (*last_t + loop_msec) - now_t;
- //Avoid problem if we somehow wrap past uint32 size (at 49.7 days!)
- if(wait_t < 0)
- wait_t = 0;
- if(wait_t > loop_msec)
- wait_t = loop_msec;
- SDL_Delay(wait_t);
- }
- *last_t = SDL_GetTicks();
-}
diff --git a/src/throttle.h b/src/throttle.h
deleted file mode 100644
index 6d55501..0000000
--- a/src/throttle.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
-
- throttle.h
-
- Description: A simple function that uses SDL_Delay() to keep loops from eating all available
- CPU
- Author: David Bruce and the TuxMath team, (C) 2009
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-
-#ifndef THROTTLE_H
-#define THROTTLE_H
-
-#include "SDL.h"
-
-// This simple function uses SDL_Delay() to wait to return until 'loop_msec'
-// milliseconds after it returned the last time. Per SDL docs, the granularity
-// is likely no better than 10 msec
-// NOTE Uint32* last_t arg added to make function thread-safe
-void Throttle(int loop_msec, Uint32* last_t);
-
-#endif
--
tuxhistory - Educational history game
More information about the Tux4kids-commits
mailing list