[Tux4kids-commits] r1674 - in branches/commonification/tuxmath/trunk: . cmake-modules data/images/status data/images/title data/images/tux data/missions/multiplay doc intl linebreak src
B. Luchen
cheezmeister-guest at alioth.debian.org
Tue Nov 24 23:54:32 UTC 2009
Author: cheezmeister-guest
Date: 2009-11-24 23:54:32 +0000 (Tue, 24 Nov 2009)
New Revision: 1674
Added:
branches/commonification/tuxmath/trunk/intl/Makefile.in
branches/commonification/tuxmath/trunk/server/
branches/commonification/tuxmath/trunk/src/mathcards.c
branches/commonification/tuxmath/trunk/src/mathcards.h
branches/commonification/tuxmath/trunk/src/network.h
branches/commonification/tuxmath/trunk/src/server.c
branches/commonification/tuxmath/trunk/src/server.h
branches/commonification/tuxmath/trunk/src/servermain.c
branches/commonification/tuxmath/trunk/src/testclient.c
branches/commonification/tuxmath/trunk/src/testclient.h
branches/commonification/tuxmath/trunk/src/throttle.c
branches/commonification/tuxmath/trunk/src/throttle.h
branches/commonification/tuxmath/trunk/src/transtruct.h
Removed:
branches/commonification/tuxmath/trunk/intl/Makefile.in
branches/commonification/tuxmath/trunk/src/fileops_media.h
branches/commonification/tuxmath/trunk/src/mathcards.c
branches/commonification/tuxmath/trunk/src/mathcards.h
Modified:
branches/commonification/tuxmath/trunk/cmake-modules/Findt4kcommon.cmake
branches/commonification/tuxmath/trunk/config.h.cmake
branches/commonification/tuxmath/trunk/configure.ac
branches/commonification/tuxmath/trunk/data/images/status/Makefile.am
branches/commonification/tuxmath/trunk/data/images/status/left.svg
branches/commonification/tuxmath/trunk/data/images/status/left_gray.svg
branches/commonification/tuxmath/trunk/data/images/status/right.svg
branches/commonification/tuxmath/trunk/data/images/status/right_gray.svg
branches/commonification/tuxmath/trunk/data/images/status/stop.svg
branches/commonification/tuxmath/trunk/data/images/status/tux4kids.svg
branches/commonification/tuxmath/trunk/data/images/title/egg.svg
branches/commonification/tuxmath/trunk/data/images/title/title1.svg
branches/commonification/tuxmath/trunk/data/images/tux/bigtux.svg
branches/commonification/tuxmath/trunk/data/missions/multiplay/ace
branches/commonification/tuxmath/trunk/data/missions/multiplay/commando
branches/commonification/tuxmath/trunk/data/missions/multiplay/scout
branches/commonification/tuxmath/trunk/doc/
branches/commonification/tuxmath/trunk/doc/README.txt
branches/commonification/tuxmath/trunk/doc/changelog
branches/commonification/tuxmath/trunk/linebreak/
branches/commonification/tuxmath/trunk/src/
branches/commonification/tuxmath/trunk/src/CMakeLists.txt
branches/commonification/tuxmath/trunk/src/Makefile.am
branches/commonification/tuxmath/trunk/src/SDL_extras.c
branches/commonification/tuxmath/trunk/src/credits.c
branches/commonification/tuxmath/trunk/src/factoroids.c
branches/commonification/tuxmath/trunk/src/fileops.h
branches/commonification/tuxmath/trunk/src/fileops_media.c
branches/commonification/tuxmath/trunk/src/game.c
branches/commonification/tuxmath/trunk/src/game.h
branches/commonification/tuxmath/trunk/src/gettext.h
branches/commonification/tuxmath/trunk/src/globals.h
branches/commonification/tuxmath/trunk/src/highscore.c
branches/commonification/tuxmath/trunk/src/highscore.h
branches/commonification/tuxmath/trunk/src/menu.c
branches/commonification/tuxmath/trunk/src/menu.h
branches/commonification/tuxmath/trunk/src/network.c
branches/commonification/tuxmath/trunk/src/options.c
branches/commonification/tuxmath/trunk/src/options.h
branches/commonification/tuxmath/trunk/src/scandir.c
branches/commonification/tuxmath/trunk/src/scandir.h
branches/commonification/tuxmath/trunk/src/setup.c
branches/commonification/tuxmath/trunk/src/titlescreen.c
branches/commonification/tuxmath/trunk/src/titlescreen.h
branches/commonification/tuxmath/trunk/src/tuxmath.c
branches/commonification/tuxmath/trunk/src/tuxmath.h
branches/commonification/tuxmath/trunk/tuxmath.desktop
Log:
Beginning to messily update commonification branch from trunk
Modified: branches/commonification/tuxmath/trunk/cmake-modules/Findt4kcommon.cmake
===================================================================
--- branches/commonification/tuxmath/trunk/cmake-modules/Findt4kcommon.cmake 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/cmake-modules/Findt4kcommon.cmake 2009-11-24 23:54:32 UTC (rev 1674)
@@ -14,10 +14,13 @@
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# T4KCOMMON_LIBRARY to override this selection.
-FIND_PATH(T4KCOMMON_INCLUDE_DIR SDL_extras.h
+FIND_PATH(T4KCOMMON_INCLUDE_DIR t4kcommon.h
$ENV{SDLDIR}/include
/usr/local/include
/usr/include
+ $ENV{SDLDIR}/include/t4kcommon
+ /usr/local/include/t4kcommon
+ /usr/include/t4kcommon
)
# I'm not sure if I should do a special casing for Apple. It is
# unlikely that other Unix systems will find the framework path.
Modified: branches/commonification/tuxmath/trunk/config.h.cmake
===================================================================
--- branches/commonification/tuxmath/trunk/config.h.cmake 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/config.h.cmake 2009-11-24 23:54:32 UTC (rev 1674)
@@ -5,6 +5,7 @@
#cmakedefine ENABLE_NLS 1
#cmakedefine SDL_Pango 1
#cmakedefine HAVE_RSVG 1
+#cmakedefine HAVE_LIBT4KCOMMON 1
/* Stuff needed for linewrap */
#cmakedefine LINEBREAK 1
Modified: branches/commonification/tuxmath/trunk/configure.ac
===================================================================
--- branches/commonification/tuxmath/trunk/configure.ac 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/configure.ac 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,7 +1,6 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_INIT([Tux Of Math Command],[1.7.3],[tuxmath-devel at lists.sourceforge.net],[tuxmath])
-AC_CONFIG_SRCDIR([src/tuxmath.c])
AC_PREREQ(2.61)
AC_CANONICAL_HOST
@@ -9,8 +8,8 @@
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE(foreign)
-AC_CONFIG_SRCDIR([config.h.in])
-AC_CONFIG_HEADER([config.h])
+AC_CONFIG_SRCDIR([src/tuxmath.c])
+AC_CONFIG_HEADERS([config.h])
NAME_VERSION=$PACKAGE-$VERSION
@@ -89,6 +88,23 @@
,
[AC_MSG_ERROR([SDL_mixer not found! http://www.libsdl.org/projects/SDL_mixer])])
+dnl SDL_Net is enabled by default
+dnl unless SDL_net is disabled at configure time.
+
+AC_ARG_WITH([sdlnet],
+ [AS_HELP_STRING([--without-sdlnet],[don't use SDL_Net even if available])],
+ [with_sdlnet=no],
+ [with_sdlnet=yes])
+
+if test "x$with_sdlnet" != xno; then
+AC_CHECK_LIB([SDL_net],
+ [SDLNet_Init],
+ ,
+ [with_sdlnet=no;
+ AC_MSG_FAILURE([SDL_Net test failed (--without-sdlnet to disable LAN support)])])
+fi
+
+
dnl SDL_Pango is enabled by default.
dnl If SDL_Pango disabled at configure time, or if we can't find it,
dnl we look for SDL_ttf:
@@ -143,19 +159,16 @@
AC_DEFINE([HAVE_RSVG],[1],[Define to 1 if you have the `libRSVG` library])
fi
-dnl Check for Tux4kids common routines
-
-AC_ARG_WITH([t4kcommon],
- [AS_HELP_STRING([--without-t4kcommon],[don't use t4kcommon even if available])],
- [with_t4kcommon=no],
- [with_t4kcommon=yes])
-
-if test "x$with_t4kcommon" = xyes; then
- AC_CHECK_LIB([t4kcommon],
- [GetScreen],
- ,
- [with_t4kcommon=no;
- AC_MSG_FAILURE([tux4kids-common test failed (--without-t4kcommon to disable)])])
+if test "x$with_rsvg" = xyes; then
+ CAIRO_CFLAGS=""
+ CAIRO_LIBS=""
+ PKG_CHECK_MODULES([CAIRO],
+ [cairo >= 1.4.10],
+ ,
+ [AC_MSG_FAILURE([cairo test failed (--without-rsvg to disable svg support)])])
+ CFLAGS="$CFLAGS $CAIRO_CFLAGS"
+ LIBS="$LIBS $CAIRO_LIBS"
+ AC_DEFINE([HAVE_RSVG],[1],[Define to 1 if you have the `libRSVG` library])
fi
dnl Check for (somewhat) higher-level math functions - needed for SDL_extras
@@ -165,6 +178,7 @@
dnl [AC_MSG_ERROR([Math library not found - functions in <math.h> may not be available.])])
+
# --------------------------------------------------------------------------------------
# Checks for header files.
# --------------------------------------------------------------------------------------
@@ -172,7 +186,7 @@
AC_FUNC_ALLOCA
AC_HEADER_DIRENT
AC_HEADER_STDC
-AC_CHECK_HEADERS([argz.h error.h errno.h fcntl.h float.h iconv.h inttypes.h langinfo.h libgen.h libintl.h limits.h locale.h malloc.h math.h stddef.h stdint.h stdio_ext.h stdlib.h string.h strings.h sys/param.h unistd.h wchar.h])
+AC_CHECK_HEADERS([argz.h error.h errno.h fcntl.h float.h iconv.h inttypes.h langinfo.h libgen.h libintl.h limits.h locale.h malloc.h math.h pthread.h stddef.h stdint.h stdio_ext.h stdlib.h string.h strings.h sys/param.h unistd.h wchar.h])
# --------------------------------------------------------------------------------------------
@@ -239,9 +253,8 @@
MINGW32_PACKAGE_DATA_DIR="data"
AC_SUBST(MINGW32_PACKAGE_DATA_DIR)
-NSI_DLL_DIR=~/tuxmath_dll
AC_ARG_WITH([dll-directory],
- AS_HELP_STRING([--with-dll-directory=path], [set the path where dll for TuxMath are [$(NSI_DLL_DIR)]]),
+ AS_HELP_STRING([--with-dll-directory=path], [provide location of DLL files needed for build [$(NSI_DLL_DIR)]]),
[dll_path=$withval],
[dll_path=no])
@@ -249,6 +262,8 @@
if test $dll_path != no; then
NSI_DLL_DIR=$dll_path
+else
+NSI_DLL_DIR=~/tuxmath_dll
fi
AC_SUBST(NSI_DLL_DIR)
Modified: branches/commonification/tuxmath/trunk/data/images/status/Makefile.am
===================================================================
--- branches/commonification/tuxmath/trunk/data/images/status/Makefile.am 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/data/images/status/Makefile.am 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,4 +1,3 @@
-## Makefile.am for tuxmath data/images/status:
## Process with AutoMake:
statusdir = $(pkgdatadir)/images/status
Property changes on: branches/commonification/tuxmath/trunk/data/images/status/left.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: branches/commonification/tuxmath/trunk/data/images/status/left_gray.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: branches/commonification/tuxmath/trunk/data/images/status/right.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: branches/commonification/tuxmath/trunk/data/images/status/right_gray.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: branches/commonification/tuxmath/trunk/data/images/status/stop.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: branches/commonification/tuxmath/trunk/data/images/status/tux4kids.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: branches/commonification/tuxmath/trunk/data/images/title/egg.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: branches/commonification/tuxmath/trunk/data/images/title/title1.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Property changes on: branches/commonification/tuxmath/trunk/data/images/tux/bigtux.svg
___________________________________________________________________
Added: svn:mergeinfo
+
Modified: branches/commonification/tuxmath/trunk/data/missions/multiplay/ace
===================================================================
--- branches/commonification/tuxmath/trunk/data/missions/multiplay/ace 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/data/missions/multiplay/ace 2009-11-24 23:54:32 UTC (rev 1674)
@@ -59,4 +59,3 @@
bonus_comet_interval = 20
allow_pause = 0
bonus_speed_ratio = 2.0
-
Modified: branches/commonification/tuxmath/trunk/data/missions/multiplay/commando
===================================================================
--- branches/commonification/tuxmath/trunk/data/missions/multiplay/commando 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/data/missions/multiplay/commando 2009-11-24 23:54:32 UTC (rev 1674)
@@ -58,4 +58,3 @@
bonus_comet_interval = 20
allow_pause = 0
bonus_speed_ratio = 2.0
-
Modified: branches/commonification/tuxmath/trunk/data/missions/multiplay/scout
===================================================================
--- branches/commonification/tuxmath/trunk/data/missions/multiplay/scout 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/data/missions/multiplay/scout 2009-11-24 23:54:32 UTC (rev 1674)
@@ -47,4 +47,3 @@
bonus_comet_interval = 10
allow_pause = 0
bonus_speed_ratio = 1.5
-
Property changes on: branches/commonification/tuxmath/trunk/doc
___________________________________________________________________
Modified: svn:ignore
- *~
*.o
CMakeFiles
Makefile
Makefile.in
+ *~
*.o
Makefile
Makefile.in
Modified: branches/commonification/tuxmath/trunk/doc/README.txt
===================================================================
--- branches/commonification/tuxmath/trunk/doc/README.txt 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/doc/README.txt 2009-11-24 23:54:32 UTC (rev 1674)
@@ -2,9 +2,9 @@
An educational math tutorial game starring Tux, the Linux Penguin
-----------------------------------------------------------------
-March 12, 2009
+October 25, 2009
-For tuxmath-1.7.2
+For tuxmath-1.7.3
Objective
---------
@@ -44,7 +44,8 @@
documentation or help screens for details.
Alternatively, simply type the command "tuxmath" at a command
- prompt (eg, in an xterm).
+ prompt (eg, in an xterm). Many command-line options are supported,
+ e.g "tuxmath -f" for fullscreen or "tuxmath -w" to run in a window.
Windows
-------
@@ -94,12 +95,52 @@
player's home directory (see below). At some point, the options will be
settable from within the game.
+ Network Game:
+ ------------------
+ Tuxmath now provides head-to-head competition over a local area network! All
+ players see the same questions, and whoever answers first gets the points for
+ that question. The game play is cooperative, however, in that all participating
+ players help defend all the igloos. Up to 16 players can participate in a single
+ game (this can be increased extremely simply with a recompilation, if desired).
+
+ To set up network play, the tuxmath server program needs to be started. Simply go
+ to Network Game->Run Server and follow the directions. You just need to type in a
+ name to identify the server to players (such as "Tux Server"). If we are able to
+ use threads on your platform, you will also be prompted to pick the lesson file to
+ be used by the server.
+
+ Once the server is running, players can connect by going to Network Game->Join Game.
+ TuxMath should automatically detect the server if it is running on the local network.
+ The player will be asked to enter a nickname, then click an arrow to indicate that
+ he/she is ready to start. When everyone has indicated that they are ready, the
+ game will start.
+
+ Note that while network play is functional, it needs more testing, and some aspects
+ have not yet been addressed:
+ - If the server program is running on more than one computer on the local network,
+ TuxMath will get confused and not connect.
+ - While a network game is in progress, do not play a non-network game on the same
+ computer - this will also confuse TuxMath (because TuxMath is not yet "thread-safe").
+ However, it is fine to participate in the network game from that computer.
+ Also, don't quit the program with the server while others are still playing
+ a network game!
+ This problem actually only occurs when we use threading to run the server,
+ meaning everything except Windows. On Windows, the server runs as a separate
+ program. The drawback is that you may leave the server running by accident,
+ so you have to go into Task Manager to kill it when you don't want it
+ running anymore.
+ - It is also possible to run the server as a separate program on all platforms
+ by typing "tuxmathserver" at the command line. This avoids any issues with
+ thread-safety, but for now the server will only use the default question list
+ settings if launched this way.
+
+
Play With Friends:
------------------
Compete with your friends by playing in a turns-based fashion! The
math difficulty levels are the same as for the "Arcade" games. Note that
- this involves rotating play at a single computer rather than multiplayer
- competition over a network (although that is an idea being considered!).
+ this involves rotating play at a single computer rather than network play,
+ as described above.
Factoroids!
-----------
Modified: branches/commonification/tuxmath/trunk/doc/changelog
===================================================================
--- branches/commonification/tuxmath/trunk/doc/changelog 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/doc/changelog 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,3 +1,23 @@
+2009.Sep.04 (svn.debian.org/tux4kids - revision 1546)
+ Data - click.wav removed due to minor concerns about whether it is really
+ under a free license (license not really known).
+
+ David Bruce <davidstuartbruce at gmail.com>
+
+2009.Sep.04 (svn.debian.org/tux4kids - revision 1489)
+ LAN project branch merged back into trunk.
+ Graphical LAN play tested and functional in branch prior to merge.
+ New menu entry code for LAN game needs to be written to actually use LAN mode
+ in trunk build.
+
+ David Bruce <davidstuartbruce at gmail.com>
+
+2009.Jul.03 (svn.debian.org/tux4kids - revision 1141)
+ LAN project - basic TCP/IP server and test client implemented in branches/lan.
+ The client and server provide multiplayer command-line tuxmath over tcp.
+ Akash Gangil <akashg1611 at gmail.com>
+ David Bruce <davidstuartbruce at gmail.com>
+
2009.Jun.24 (svn.debian.org/tux4kids - revision xxx)
i18n - new languages from launchpad.
Asturian by Xuacu
@@ -10,10 +30,6 @@
2009.Mar.12 (svn.debian.org/tux4kids - revision 954
one-liner tweak of campaign.c to fix display problem in scrolling text
that strangely is only apparent in Windows and Mac
-
- David Bruce <davidstuartbruce at gmail.com>
-
-
2009.Mar.12 (svn.debian.org/tux4kids - revision 937)
Version 1.7.2
Build- some minor tweaks to tuxmath.desktop and specfiles.
Deleted: branches/commonification/tuxmath/trunk/intl/Makefile.in
===================================================================
--- branches/commonification/tuxmath/trunk/intl/Makefile.in 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/intl/Makefile.in 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,587 +0,0 @@
-# Makefile for directory with message catalog handling library of GNU gettext
-# Copyright (C) 1995-1998, 2000-2007 Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Library General Public License as published
-# by the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Library General Public License for more details.
-#
-# You should have received a copy of the GNU Library General Public
-# License along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-PACKAGE = @PACKAGE@
-VERSION = @VERSION@
-
-SHELL = /bin/sh
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-top_builddir = ..
-
-# The VPATH variables allows builds with $builddir != $srcdir, assuming a
-# 'make' program that supports VPATH (such as GNU make). This line is removed
-# by autoconf automatically when "$(srcdir)" = ".".
-# In this directory, the VPATH handling is particular:
-# 1. If INTL_LIBTOOL_SUFFIX_PREFIX is 'l' (indicating a build with libtool),
-# the .c -> .lo rules carefully use $(srcdir), so that VPATH can be omitted.
-# 2. If PACKAGE = gettext-tools, VPATH _must_ be omitted, because otherwise
-# 'make' does the wrong thing if GNU gettext was configured with
-# "./configure --srcdir=`pwd`", namely it gets confused by the .lo and .la
-# files it finds in srcdir = ../../gettext-runtime/intl.
-VPATH = $(srcdir)
-
-prefix = @prefix@
-exec_prefix = @exec_prefix@
-transform = @program_transform_name@
-libdir = @libdir@
-includedir = @includedir@
-datarootdir = @datarootdir@
-datadir = @datadir@
-localedir = $(datadir)/locale
-gettextsrcdir = $(datadir)/gettext/intl
-aliaspath = $(localedir)
-subdir = intl
-
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
-
-# We use $(mkdir_p).
-# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as
-# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions,
-# @install_sh@ does not start with $(SHELL), so we add it.
-# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined
-# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake
-# versions, $(mkinstalldirs) and $(install_sh) are unused.
-mkinstalldirs = $(SHELL) @install_sh@ -d
-install_sh = $(SHELL) @install_sh@
-MKDIR_P = @MKDIR_P@
-mkdir_p = @mkdir_p@
-
-l = @INTL_LIBTOOL_SUFFIX_PREFIX@
-
-AR = ar
-CC = @CC@
-LIBTOOL = @LIBTOOL@
-RANLIB = @RANLIB@
-YACC = @INTLBISON@ -y -d
-YFLAGS = --name-prefix=__gettext
-WINDRES = @WINDRES@
-
-# -DBUILDING_LIBINTL: Change expansion of LIBINTL_DLL_EXPORTED macro.
-# -DBUILDING_DLL: Change expansion of RELOCATABLE_DLL_EXPORTED macro.
-DEFS = -DLOCALEDIR=\"$(localedir)\" -DLOCALE_ALIAS_PATH=\"$(aliaspath)\" \
--DLIBDIR=\"$(libdir)\" -DBUILDING_LIBINTL -DBUILDING_DLL -DIN_LIBINTL \
--DENABLE_RELOCATABLE=1 -DIN_LIBRARY -DINSTALLDIR=\"$(libdir)\" -DNO_XMALLOC \
--Dset_relocation_prefix=libintl_set_relocation_prefix \
--Drelocate=libintl_relocate \
--DDEPENDS_ON_LIBICONV=1 @DEFS@
-CPPFLAGS = @CPPFLAGS@
-CFLAGS = @CFLAGS@ @CFLAG_VISIBILITY@
-LDFLAGS = @LDFLAGS@ $(LDFLAGS_ at WOE32DLL@)
-LDFLAGS_yes = -Wl,--export-all-symbols
-LDFLAGS_no =
-LIBS = @LIBS@
-
-COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS)
-
-HEADERS = \
- gmo.h \
- gettextP.h \
- hash-string.h \
- loadinfo.h \
- plural-exp.h \
- eval-plural.h \
- localcharset.h \
- lock.h \
- relocatable.h \
- tsearch.h tsearch.c \
- xsize.h \
- printf-args.h printf-args.c \
- printf-parse.h wprintf-parse.h printf-parse.c \
- vasnprintf.h vasnwprintf.h vasnprintf.c \
- os2compat.h \
- libgnuintl.h.in
-SOURCES = \
- bindtextdom.c \
- dcgettext.c \
- dgettext.c \
- gettext.c \
- finddomain.c \
- hash-string.c \
- loadmsgcat.c \
- localealias.c \
- textdomain.c \
- l10nflist.c \
- explodename.c \
- dcigettext.c \
- dcngettext.c \
- dngettext.c \
- ngettext.c \
- plural.y \
- plural-exp.c \
- localcharset.c \
- lock.c \
- relocatable.c \
- langprefs.c \
- localename.c \
- log.c \
- printf.c \
- version.c \
- osdep.c \
- os2compat.c \
- intl-exports.c \
- intl-compat.c
-OBJECTS = \
- bindtextdom.$lo \
- dcgettext.$lo \
- dgettext.$lo \
- gettext.$lo \
- finddomain.$lo \
- hash-string.$lo \
- loadmsgcat.$lo \
- localealias.$lo \
- textdomain.$lo \
- l10nflist.$lo \
- explodename.$lo \
- dcigettext.$lo \
- dcngettext.$lo \
- dngettext.$lo \
- ngettext.$lo \
- plural.$lo \
- plural-exp.$lo \
- localcharset.$lo \
- lock.$lo \
- relocatable.$lo \
- langprefs.$lo \
- localename.$lo \
- log.$lo \
- printf.$lo \
- version.$lo \
- osdep.$lo \
- intl-compat.$lo
-OBJECTS_RES_yes = libintl.res
-OBJECTS_RES_no =
-DISTFILES.common = Makefile.in \
-config.charset locale.alias ref-add.sin ref-del.sin export.h libintl.rc \
-$(HEADERS) $(SOURCES)
-DISTFILES.generated = plural.c
-DISTFILES.normal = VERSION
-DISTFILES.gettext = COPYING.LIB-2.0 COPYING.LIB-2.1 libintl.glibc README.woe32
-DISTFILES.obsolete = xopen-msg.sed linux-msg.sed po2tbl.sed.in cat-compat.c \
-COPYING.LIB-2 gettext.h libgettext.h plural-eval.c libgnuintl.h \
-libgnuintl.h_vms Makefile.vms libgnuintl.h.msvc-static \
-libgnuintl.h.msvc-shared Makefile.msvc
-
-all: all- at USE_INCLUDED_LIBINTL@
-all-yes: libintl.$la libintl.h charset.alias ref-add.sed ref-del.sed
-all-no: all-no- at BUILD_INCLUDED_LIBINTL@
-all-no-yes: libgnuintl.$la
-all-no-no:
-
-libintl.a libgnuintl.a: $(OBJECTS)
- rm -f $@
- $(AR) cru $@ $(OBJECTS)
- $(RANLIB) $@
-
-libintl.la libgnuintl.la: $(OBJECTS) $(OBJECTS_RES_ at WOE32@)
- $(LIBTOOL) --mode=link \
- $(CC) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS) $(LDFLAGS) -o $@ \
- $(OBJECTS) @LTLIBICONV@ @INTL_MACOSX_LIBS@ $(LIBS) @LTLIBTHREAD@ @LTLIBC@ \
- $(OBJECTS_RES_ at WOE32@) \
- -version-info $(LTV_CURRENT):$(LTV_REVISION):$(LTV_AGE) \
- -rpath $(libdir) \
- -no-undefined
-
-# Libtool's library version information for libintl.
-# Before making a gettext release, the gettext maintainer must change this
-# according to the libtool documentation, section "Library interface versions".
-# Maintainers of other packages that include the intl directory must *not*
-# change these values.
-LTV_CURRENT=8
-LTV_REVISION=2
-LTV_AGE=0
-
-.SUFFIXES:
-.SUFFIXES: .c .y .o .lo .sin .sed
-
-.c.o:
- $(COMPILE) $<
-
-.y.c:
- $(YACC) $(YFLAGS) --output $@ $<
- rm -f $*.h
-
-bindtextdom.lo: $(srcdir)/bindtextdom.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/bindtextdom.c
-dcgettext.lo: $(srcdir)/dcgettext.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcgettext.c
-dgettext.lo: $(srcdir)/dgettext.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dgettext.c
-gettext.lo: $(srcdir)/gettext.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/gettext.c
-finddomain.lo: $(srcdir)/finddomain.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/finddomain.c
-hash-string.lo: $(srcdir)/hash-string.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/hash-string.c
-loadmsgcat.lo: $(srcdir)/loadmsgcat.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/loadmsgcat.c
-localealias.lo: $(srcdir)/localealias.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localealias.c
-textdomain.lo: $(srcdir)/textdomain.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/textdomain.c
-l10nflist.lo: $(srcdir)/l10nflist.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/l10nflist.c
-explodename.lo: $(srcdir)/explodename.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/explodename.c
-dcigettext.lo: $(srcdir)/dcigettext.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcigettext.c
-dcngettext.lo: $(srcdir)/dcngettext.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcngettext.c
-dngettext.lo: $(srcdir)/dngettext.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dngettext.c
-ngettext.lo: $(srcdir)/ngettext.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/ngettext.c
-plural.lo: $(srcdir)/plural.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/plural.c
-plural-exp.lo: $(srcdir)/plural-exp.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/plural-exp.c
-localcharset.lo: $(srcdir)/localcharset.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localcharset.c
-lock.lo: $(srcdir)/lock.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/lock.c
-relocatable.lo: $(srcdir)/relocatable.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/relocatable.c
-langprefs.lo: $(srcdir)/langprefs.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/langprefs.c
-localename.lo: $(srcdir)/localename.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localename.c
-log.lo: $(srcdir)/log.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/log.c
-printf.lo: $(srcdir)/printf.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/printf.c
-version.lo: $(srcdir)/version.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/version.c
-osdep.lo: $(srcdir)/osdep.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/osdep.c
-intl-compat.lo: $(srcdir)/intl-compat.c
- $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/intl-compat.c
-
-# This rule is executed only on Woe32 systems.
-# The following sed expressions come from the windres-options script. They are
-# inlined here, so that they can be written in a Makefile without requiring a
-# temporary file. They must contain literal newlines rather than semicolons,
-# so that they work with the sed-3.02 that is shipped with MSYS. We can use
-# GNU bash's $'\n' syntax to obtain such a newline.
-libintl.res: $(srcdir)/libintl.rc
- nl=$$'\n'; \
- sed_extract_major='/^[0-9]/{'$${nl}'s/^\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
- sed_extract_minor='/^[0-9][0-9]*[.][0-9]/{'$${nl}'s/^[0-9]*[.]\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
- sed_extract_subminor='/^[0-9][0-9]*[.][0-9][0-9]*[.][0-9]/{'$${nl}'s/^[0-9]*[.][0-9]*[.]\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
- $(WINDRES) \
- "-DPACKAGE_VERSION_STRING=\\\"$(VERSION)\\\"" \
- "-DPACKAGE_VERSION_MAJOR="`echo '$(VERSION)' | sed -n -e "$$sed_extract_major"` \
- "-DPACKAGE_VERSION_MINOR="`echo '$(VERSION)' | sed -n -e "$$sed_extract_minor"` \
- "-DPACKAGE_VERSION_SUBMINOR="`echo '$(VERSION)' | sed -n -e "$$sed_extract_subminor"` \
- -i $(srcdir)/libintl.rc -o libintl.res --output-format=coff
-
-ref-add.sed: $(srcdir)/ref-add.sin
- sed -e '/^#/d' -e 's/@''PACKAGE''@/@PACKAGE@/g' $(srcdir)/ref-add.sin > t-ref-add.sed
- mv t-ref-add.sed ref-add.sed
-ref-del.sed: $(srcdir)/ref-del.sin
- sed -e '/^#/d' -e 's/@''PACKAGE''@/@PACKAGE@/g' $(srcdir)/ref-del.sin > t-ref-del.sed
- mv t-ref-del.sed ref-del.sed
-
-INCLUDES = -I. -I$(srcdir) -I..
-
-libgnuintl.h: $(srcdir)/libgnuintl.h.in
- sed -e '/IN_LIBGLOCALE/d' \
- -e 's,@''HAVE_POSIX_PRINTF''@, at HAVE_POSIX_PRINTF@,g' \
- -e 's,@''HAVE_ASPRINTF''@, at HAVE_ASPRINTF@,g' \
- -e 's,@''HAVE_SNPRINTF''@, at HAVE_SNPRINTF@,g' \
- -e 's,@''HAVE_WPRINTF''@, at HAVE_WPRINTF@,g' \
- < $(srcdir)/libgnuintl.h.in \
- | if test '@WOE32DLL@' = yes; then \
- sed -e 's/extern \([^()]*\);/extern __declspec (dllimport) \1;/'; \
- else \
- cat; \
- fi \
- | sed -e 's/extern \([^"]\)/extern LIBINTL_DLL_EXPORTED \1/' \
- -e "/#define _LIBINTL_H/r $(srcdir)/export.h" \
- | sed -e 's,@''HAVE_VISIBILITY''@, at HAVE_VISIBILITY@,g' \
- > libgnuintl.h
-
-libintl.h: $(srcdir)/libgnuintl.h.in
- sed -e '/IN_LIBGLOCALE/d' \
- -e 's,@''HAVE_POSIX_PRINTF''@, at HAVE_POSIX_PRINTF@,g' \
- -e 's,@''HAVE_ASPRINTF''@, at HAVE_ASPRINTF@,g' \
- -e 's,@''HAVE_SNPRINTF''@, at HAVE_SNPRINTF@,g' \
- -e 's,@''HAVE_WPRINTF''@, at HAVE_WPRINTF@,g' \
- < $(srcdir)/libgnuintl.h.in > libintl.h
-
-charset.alias: $(srcdir)/config.charset
- $(SHELL) $(srcdir)/config.charset '@host@' > t-$@
- mv t-$@ $@
-
-check: all
-
-# We must not install the libintl.h/libintl.a files if we are on a
-# system which has the GNU gettext() function in its C library or in a
-# separate library.
-# If you want to use the one which comes with this version of the
-# package, you have to use `configure --with-included-gettext'.
-install: install-exec install-data
-install-exec: all
- if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \
- && test '@USE_INCLUDED_LIBINTL@' = yes; then \
- $(mkdir_p) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir); \
- $(INSTALL_DATA) libintl.h $(DESTDIR)$(includedir)/libintl.h; \
- $(LIBTOOL) --mode=install \
- $(INSTALL_DATA) libintl.$la $(DESTDIR)$(libdir)/libintl.$la; \
- if test "@RELOCATABLE@" = yes; then \
- dependencies=`sed -n -e 's,^dependency_libs=\(.*\),\1,p' < $(DESTDIR)$(libdir)/libintl.la | sed -e "s,^',," -e "s,'\$$,,"`; \
- if test -n "$$dependencies"; then \
- rm -f $(DESTDIR)$(libdir)/libintl.la; \
- fi; \
- fi; \
- else \
- : ; \
- fi
- if test "$(PACKAGE)" = "gettext-tools" \
- && test '@USE_INCLUDED_LIBINTL@' = no \
- && test @GLIBC2@ != no; then \
- $(mkdir_p) $(DESTDIR)$(libdir); \
- $(LIBTOOL) --mode=install \
- $(INSTALL_DATA) libgnuintl.$la $(DESTDIR)$(libdir)/libgnuintl.$la; \
- rm -f $(DESTDIR)$(libdir)/preloadable_libintl.so; \
- $(INSTALL_DATA) $(DESTDIR)$(libdir)/libgnuintl.so $(DESTDIR)$(libdir)/preloadable_libintl.so; \
- $(LIBTOOL) --mode=uninstall \
- rm -f $(DESTDIR)$(libdir)/libgnuintl.$la; \
- else \
- : ; \
- fi
- if test '@USE_INCLUDED_LIBINTL@' = yes; then \
- test @GLIBC21@ != no || $(mkdir_p) $(DESTDIR)$(libdir); \
- temp=$(DESTDIR)$(libdir)/t-charset.alias; \
- dest=$(DESTDIR)$(libdir)/charset.alias; \
- if test -f $(DESTDIR)$(libdir)/charset.alias; then \
- orig=$(DESTDIR)$(libdir)/charset.alias; \
- sed -f ref-add.sed $$orig > $$temp; \
- $(INSTALL_DATA) $$temp $$dest; \
- rm -f $$temp; \
- else \
- if test @GLIBC21@ = no; then \
- orig=charset.alias; \
- sed -f ref-add.sed $$orig > $$temp; \
- $(INSTALL_DATA) $$temp $$dest; \
- rm -f $$temp; \
- fi; \
- fi; \
- $(mkdir_p) $(DESTDIR)$(localedir); \
- test -f $(DESTDIR)$(localedir)/locale.alias \
- && orig=$(DESTDIR)$(localedir)/locale.alias \
- || orig=$(srcdir)/locale.alias; \
- temp=$(DESTDIR)$(localedir)/t-locale.alias; \
- dest=$(DESTDIR)$(localedir)/locale.alias; \
- sed -f ref-add.sed $$orig > $$temp; \
- $(INSTALL_DATA) $$temp $$dest; \
- rm -f $$temp; \
- else \
- : ; \
- fi
-install-data: all
- if test "$(PACKAGE)" = "gettext-tools"; then \
- $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
- $(INSTALL_DATA) VERSION $(DESTDIR)$(gettextsrcdir)/VERSION; \
- $(INSTALL_DATA) ChangeLog.inst $(DESTDIR)$(gettextsrcdir)/ChangeLog; \
- dists="COPYING.LIB-2.0 COPYING.LIB-2.1 $(DISTFILES.common)"; \
- for file in $$dists; do \
- $(INSTALL_DATA) $(srcdir)/$$file \
- $(DESTDIR)$(gettextsrcdir)/$$file; \
- done; \
- chmod a+x $(DESTDIR)$(gettextsrcdir)/config.charset; \
- dists="$(DISTFILES.generated)"; \
- for file in $$dists; do \
- if test -f $$file; then dir=.; else dir=$(srcdir); fi; \
- $(INSTALL_DATA) $$dir/$$file \
- $(DESTDIR)$(gettextsrcdir)/$$file; \
- done; \
- dists="$(DISTFILES.obsolete)"; \
- for file in $$dists; do \
- rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
- done; \
- else \
- : ; \
- fi
-
-install-strip: install
-
-install-dvi install-html install-info install-ps install-pdf:
-
-installdirs:
- if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \
- && test '@USE_INCLUDED_LIBINTL@' = yes; then \
- $(mkdir_p) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir); \
- else \
- : ; \
- fi
- if test "$(PACKAGE)" = "gettext-tools" \
- && test '@USE_INCLUDED_LIBINTL@' = no \
- && test @GLIBC2@ != no; then \
- $(mkdir_p) $(DESTDIR)$(libdir); \
- else \
- : ; \
- fi
- if test '@USE_INCLUDED_LIBINTL@' = yes; then \
- test @GLIBC21@ != no || $(mkdir_p) $(DESTDIR)$(libdir); \
- $(mkdir_p) $(DESTDIR)$(localedir); \
- else \
- : ; \
- fi
- if test "$(PACKAGE)" = "gettext-tools"; then \
- $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
- else \
- : ; \
- fi
-
-# Define this as empty until I found a useful application.
-installcheck:
-
-uninstall:
- if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \
- && test '@USE_INCLUDED_LIBINTL@' = yes; then \
- rm -f $(DESTDIR)$(includedir)/libintl.h; \
- $(LIBTOOL) --mode=uninstall \
- rm -f $(DESTDIR)$(libdir)/libintl.$la; \
- else \
- : ; \
- fi
- if test "$(PACKAGE)" = "gettext-tools" \
- && test '@USE_INCLUDED_LIBINTL@' = no \
- && test @GLIBC2@ != no; then \
- rm -f $(DESTDIR)$(libdir)/preloadable_libintl.so; \
- else \
- : ; \
- fi
- if test '@USE_INCLUDED_LIBINTL@' = yes; then \
- if test -f $(DESTDIR)$(libdir)/charset.alias; then \
- temp=$(DESTDIR)$(libdir)/t-charset.alias; \
- dest=$(DESTDIR)$(libdir)/charset.alias; \
- sed -f ref-del.sed $$dest > $$temp; \
- if grep '^# Packages using this file: $$' $$temp > /dev/null; then \
- rm -f $$dest; \
- else \
- $(INSTALL_DATA) $$temp $$dest; \
- fi; \
- rm -f $$temp; \
- fi; \
- if test -f $(DESTDIR)$(localedir)/locale.alias; then \
- temp=$(DESTDIR)$(localedir)/t-locale.alias; \
- dest=$(DESTDIR)$(localedir)/locale.alias; \
- sed -f ref-del.sed $$dest > $$temp; \
- if grep '^# Packages using this file: $$' $$temp > /dev/null; then \
- rm -f $$dest; \
- else \
- $(INSTALL_DATA) $$temp $$dest; \
- fi; \
- rm -f $$temp; \
- fi; \
- else \
- : ; \
- fi
- if test "$(PACKAGE)" = "gettext-tools"; then \
- for file in VERSION ChangeLog COPYING.LIB-2.0 COPYING.LIB-2.1 $(DISTFILES.common) $(DISTFILES.generated); do \
- rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
- done; \
- else \
- : ; \
- fi
-
-info dvi ps pdf html:
-
-$(OBJECTS): ../config.h libgnuintl.h
-bindtextdom.$lo dcgettext.$lo dcigettext.$lo dcngettext.$lo dgettext.$lo dngettext.$lo finddomain.$lo gettext.$lo intl-compat.$lo loadmsgcat.$lo localealias.$lo ngettext.$lo textdomain.$lo: $(srcdir)/gettextP.h $(srcdir)/gmo.h $(srcdir)/loadinfo.h
-hash-string.$lo dcigettext.$lo loadmsgcat.$lo: $(srcdir)/hash-string.h
-explodename.$lo l10nflist.$lo: $(srcdir)/loadinfo.h
-dcigettext.$lo loadmsgcat.$lo plural.$lo plural-exp.$lo: $(srcdir)/plural-exp.h
-dcigettext.$lo: $(srcdir)/eval-plural.h
-localcharset.$lo: $(srcdir)/localcharset.h
-bindtextdom.$lo dcigettext.$lo finddomain.$lo loadmsgcat.$lo localealias.$lo lock.$lo log.$lo: $(srcdir)/lock.h
-localealias.$lo localcharset.$lo relocatable.$lo: $(srcdir)/relocatable.h
-printf.$lo: $(srcdir)/printf-args.h $(srcdir)/printf-args.c $(srcdir)/printf-parse.h $(srcdir)/wprintf-parse.h $(srcdir)/xsize.h $(srcdir)/printf-parse.c $(srcdir)/vasnprintf.h $(srcdir)/vasnwprintf.h $(srcdir)/vasnprintf.c
-
-# A bison-2.1 generated plural.c includes <libintl.h> if ENABLE_NLS.
-PLURAL_DEPS_yes = libintl.h
-PLURAL_DEPS_no =
-plural.$lo: $(PLURAL_DEPS_ at USE_INCLUDED_LIBINTL@)
-
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES)
- here=`pwd`; cd $(srcdir) && etags -o $$here/TAGS $(HEADERS) $(SOURCES)
-
-ctags: CTAGS
-
-CTAGS: $(HEADERS) $(SOURCES)
- here=`pwd`; cd $(srcdir) && ctags -o $$here/CTAGS $(HEADERS) $(SOURCES)
-
-id: ID
-
-ID: $(HEADERS) $(SOURCES)
- here=`pwd`; cd $(srcdir) && mkid -f$$here/ID $(HEADERS) $(SOURCES)
-
-
-mostlyclean:
- rm -f *.a *.la *.o *.obj *.lo libintl.res core core.*
- rm -f libgnuintl.h libintl.h charset.alias ref-add.sed ref-del.sed
- rm -f -r .libs _libs
-
-clean: mostlyclean
-
-distclean: clean
- rm -f Makefile ID TAGS
- if test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; then \
- rm -f ChangeLog.inst $(DISTFILES.normal); \
- else \
- : ; \
- fi
-
-maintainer-clean: distclean
- @echo "This command is intended for maintainers to use;"
- @echo "it deletes files that may require special tools to rebuild."
-
-
-# GNU gettext needs not contain the file `VERSION' but contains some
-# other files which should not be distributed in other packages.
-distdir = ../$(PACKAGE)-$(VERSION)/$(subdir)
-dist distdir: Makefile
- if test "$(PACKAGE)" = "gettext-tools"; then \
- : ; \
- else \
- if test "$(PACKAGE)" = "gettext-runtime"; then \
- additional="$(DISTFILES.gettext)"; \
- else \
- additional="$(DISTFILES.normal)"; \
- fi; \
- $(MAKE) $(DISTFILES.common) $(DISTFILES.generated) $$additional; \
- for file in ChangeLog $(DISTFILES.common) $(DISTFILES.generated) $$additional; do \
- if test -f $$file; then dir=.; else dir=$(srcdir); fi; \
- cp -p $$dir/$$file $(distdir) || test $$file = Makefile.in || exit 1; \
- done; \
- fi
-
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
- cd $(top_builddir) && $(SHELL) ./config.status
-# This would be more efficient, but doesn't work any more with autoconf-2.57,
-# when AC_CONFIG_FILES([intl/Makefile:somedir/Makefile.in]) is used.
-# cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
-
-# Tell versions [3.59,3.63) of GNU make not to export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
Copied: branches/commonification/tuxmath/trunk/intl/Makefile.in (from rev 1657, tuxmath/trunk/intl/Makefile.in)
===================================================================
--- branches/commonification/tuxmath/trunk/intl/Makefile.in (rev 0)
+++ branches/commonification/tuxmath/trunk/intl/Makefile.in 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,587 @@
+# Makefile for directory with message catalog handling library of GNU gettext
+# Copyright (C) 1995-1998, 2000-2007 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Library General Public License as published
+# by the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = ..
+
+# The VPATH variables allows builds with $builddir != $srcdir, assuming a
+# 'make' program that supports VPATH (such as GNU make). This line is removed
+# by autoconf automatically when "$(srcdir)" = ".".
+# In this directory, the VPATH handling is particular:
+# 1. If INTL_LIBTOOL_SUFFIX_PREFIX is 'l' (indicating a build with libtool),
+# the .c -> .lo rules carefully use $(srcdir), so that VPATH can be omitted.
+# 2. If PACKAGE = gettext-tools, VPATH _must_ be omitted, because otherwise
+# 'make' does the wrong thing if GNU gettext was configured with
+# "./configure --srcdir=`pwd`", namely it gets confused by the .lo and .la
+# files it finds in srcdir = ../../gettext-runtime/intl.
+VPATH = $(srcdir)
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+transform = @program_transform_name@
+libdir = @libdir@
+includedir = @includedir@
+datarootdir = @datarootdir@
+datadir = @datadir@
+localedir = $(datadir)/locale
+gettextsrcdir = $(datadir)/gettext/intl
+aliaspath = $(localedir)
+subdir = intl
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+
+# We use $(mkdir_p).
+# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as
+# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions,
+# @install_sh@ does not start with $(SHELL), so we add it.
+# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined
+# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake
+# versions, $(mkinstalldirs) and $(install_sh) are unused.
+mkinstalldirs = $(SHELL) @install_sh@ -d
+install_sh = $(SHELL) @install_sh@
+MKDIR_P = @MKDIR_P@
+mkdir_p = @mkdir_p@
+
+l = @INTL_LIBTOOL_SUFFIX_PREFIX@
+
+AR = ar
+CC = @CC@
+LIBTOOL = @LIBTOOL@
+RANLIB = @RANLIB@
+YACC = @INTLBISON@ -y -d
+YFLAGS = --name-prefix=__gettext
+WINDRES = @WINDRES@
+
+# -DBUILDING_LIBINTL: Change expansion of LIBINTL_DLL_EXPORTED macro.
+# -DBUILDING_DLL: Change expansion of RELOCATABLE_DLL_EXPORTED macro.
+DEFS = -DLOCALEDIR=\"$(localedir)\" -DLOCALE_ALIAS_PATH=\"$(aliaspath)\" \
+-DLIBDIR=\"$(libdir)\" -DBUILDING_LIBINTL -DBUILDING_DLL -DIN_LIBINTL \
+-DENABLE_RELOCATABLE=1 -DIN_LIBRARY -DINSTALLDIR=\"$(libdir)\" -DNO_XMALLOC \
+-Dset_relocation_prefix=libintl_set_relocation_prefix \
+-Drelocate=libintl_relocate \
+-DDEPENDS_ON_LIBICONV=1 @DEFS@
+CPPFLAGS = @CPPFLAGS@
+CFLAGS = @CFLAGS@ @CFLAG_VISIBILITY@
+LDFLAGS = @LDFLAGS@ $(LDFLAGS_ at WOE32DLL@)
+LDFLAGS_yes = -Wl,--export-all-symbols
+LDFLAGS_no =
+LIBS = @LIBS@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS)
+
+HEADERS = \
+ gmo.h \
+ gettextP.h \
+ hash-string.h \
+ loadinfo.h \
+ plural-exp.h \
+ eval-plural.h \
+ localcharset.h \
+ lock.h \
+ relocatable.h \
+ tsearch.h tsearch.c \
+ xsize.h \
+ printf-args.h printf-args.c \
+ printf-parse.h wprintf-parse.h printf-parse.c \
+ vasnprintf.h vasnwprintf.h vasnprintf.c \
+ os2compat.h \
+ libgnuintl.h.in
+SOURCES = \
+ bindtextdom.c \
+ dcgettext.c \
+ dgettext.c \
+ gettext.c \
+ finddomain.c \
+ hash-string.c \
+ loadmsgcat.c \
+ localealias.c \
+ textdomain.c \
+ l10nflist.c \
+ explodename.c \
+ dcigettext.c \
+ dcngettext.c \
+ dngettext.c \
+ ngettext.c \
+ plural.y \
+ plural-exp.c \
+ localcharset.c \
+ lock.c \
+ relocatable.c \
+ langprefs.c \
+ localename.c \
+ log.c \
+ printf.c \
+ version.c \
+ osdep.c \
+ os2compat.c \
+ intl-exports.c \
+ intl-compat.c
+OBJECTS = \
+ bindtextdom.$lo \
+ dcgettext.$lo \
+ dgettext.$lo \
+ gettext.$lo \
+ finddomain.$lo \
+ hash-string.$lo \
+ loadmsgcat.$lo \
+ localealias.$lo \
+ textdomain.$lo \
+ l10nflist.$lo \
+ explodename.$lo \
+ dcigettext.$lo \
+ dcngettext.$lo \
+ dngettext.$lo \
+ ngettext.$lo \
+ plural.$lo \
+ plural-exp.$lo \
+ localcharset.$lo \
+ lock.$lo \
+ relocatable.$lo \
+ langprefs.$lo \
+ localename.$lo \
+ log.$lo \
+ printf.$lo \
+ version.$lo \
+ osdep.$lo \
+ intl-compat.$lo
+OBJECTS_RES_yes = libintl.res
+OBJECTS_RES_no =
+DISTFILES.common = Makefile.in \
+config.charset locale.alias ref-add.sin ref-del.sin export.h libintl.rc \
+$(HEADERS) $(SOURCES)
+DISTFILES.generated = plural.c
+DISTFILES.normal = VERSION
+DISTFILES.gettext = COPYING.LIB-2.0 COPYING.LIB-2.1 libintl.glibc README.woe32
+DISTFILES.obsolete = xopen-msg.sed linux-msg.sed po2tbl.sed.in cat-compat.c \
+COPYING.LIB-2 gettext.h libgettext.h plural-eval.c libgnuintl.h \
+libgnuintl.h_vms Makefile.vms libgnuintl.h.msvc-static \
+libgnuintl.h.msvc-shared Makefile.msvc
+
+all: all- at USE_INCLUDED_LIBINTL@
+all-yes: libintl.$la libintl.h charset.alias ref-add.sed ref-del.sed
+all-no: all-no- at BUILD_INCLUDED_LIBINTL@
+all-no-yes: libgnuintl.$la
+all-no-no:
+
+libintl.a libgnuintl.a: $(OBJECTS)
+ rm -f $@
+ $(AR) cru $@ $(OBJECTS)
+ $(RANLIB) $@
+
+libintl.la libgnuintl.la: $(OBJECTS) $(OBJECTS_RES_ at WOE32@)
+ $(LIBTOOL) --mode=link \
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS) $(LDFLAGS) -o $@ \
+ $(OBJECTS) @LTLIBICONV@ @INTL_MACOSX_LIBS@ $(LIBS) @LTLIBTHREAD@ @LTLIBC@ \
+ $(OBJECTS_RES_ at WOE32@) \
+ -version-info $(LTV_CURRENT):$(LTV_REVISION):$(LTV_AGE) \
+ -rpath $(libdir) \
+ -no-undefined
+
+# Libtool's library version information for libintl.
+# Before making a gettext release, the gettext maintainer must change this
+# according to the libtool documentation, section "Library interface versions".
+# Maintainers of other packages that include the intl directory must *not*
+# change these values.
+LTV_CURRENT=8
+LTV_REVISION=2
+LTV_AGE=0
+
+.SUFFIXES:
+.SUFFIXES: .c .y .o .lo .sin .sed
+
+.c.o:
+ $(COMPILE) $<
+
+.y.c:
+ $(YACC) $(YFLAGS) --output $@ $<
+ rm -f $*.h
+
+bindtextdom.lo: $(srcdir)/bindtextdom.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/bindtextdom.c
+dcgettext.lo: $(srcdir)/dcgettext.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcgettext.c
+dgettext.lo: $(srcdir)/dgettext.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dgettext.c
+gettext.lo: $(srcdir)/gettext.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/gettext.c
+finddomain.lo: $(srcdir)/finddomain.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/finddomain.c
+hash-string.lo: $(srcdir)/hash-string.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/hash-string.c
+loadmsgcat.lo: $(srcdir)/loadmsgcat.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/loadmsgcat.c
+localealias.lo: $(srcdir)/localealias.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localealias.c
+textdomain.lo: $(srcdir)/textdomain.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/textdomain.c
+l10nflist.lo: $(srcdir)/l10nflist.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/l10nflist.c
+explodename.lo: $(srcdir)/explodename.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/explodename.c
+dcigettext.lo: $(srcdir)/dcigettext.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcigettext.c
+dcngettext.lo: $(srcdir)/dcngettext.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dcngettext.c
+dngettext.lo: $(srcdir)/dngettext.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/dngettext.c
+ngettext.lo: $(srcdir)/ngettext.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/ngettext.c
+plural.lo: $(srcdir)/plural.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/plural.c
+plural-exp.lo: $(srcdir)/plural-exp.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/plural-exp.c
+localcharset.lo: $(srcdir)/localcharset.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localcharset.c
+lock.lo: $(srcdir)/lock.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/lock.c
+relocatable.lo: $(srcdir)/relocatable.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/relocatable.c
+langprefs.lo: $(srcdir)/langprefs.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/langprefs.c
+localename.lo: $(srcdir)/localename.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/localename.c
+log.lo: $(srcdir)/log.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/log.c
+printf.lo: $(srcdir)/printf.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/printf.c
+version.lo: $(srcdir)/version.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/version.c
+osdep.lo: $(srcdir)/osdep.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/osdep.c
+intl-compat.lo: $(srcdir)/intl-compat.c
+ $(LIBTOOL) --mode=compile $(COMPILE) $(srcdir)/intl-compat.c
+
+# This rule is executed only on Woe32 systems.
+# The following sed expressions come from the windres-options script. They are
+# inlined here, so that they can be written in a Makefile without requiring a
+# temporary file. They must contain literal newlines rather than semicolons,
+# so that they work with the sed-3.02 that is shipped with MSYS. We can use
+# GNU bash's $'\n' syntax to obtain such a newline.
+libintl.res: $(srcdir)/libintl.rc
+ nl=$$'\n'; \
+ sed_extract_major='/^[0-9]/{'$${nl}'s/^\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
+ sed_extract_minor='/^[0-9][0-9]*[.][0-9]/{'$${nl}'s/^[0-9]*[.]\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
+ sed_extract_subminor='/^[0-9][0-9]*[.][0-9][0-9]*[.][0-9]/{'$${nl}'s/^[0-9]*[.][0-9]*[.]\([0-9]*\).*/\1/p'$${nl}q$${nl}'}'$${nl}'c\'$${nl}0$${nl}q; \
+ $(WINDRES) \
+ "-DPACKAGE_VERSION_STRING=\\\"$(VERSION)\\\"" \
+ "-DPACKAGE_VERSION_MAJOR="`echo '$(VERSION)' | sed -n -e "$$sed_extract_major"` \
+ "-DPACKAGE_VERSION_MINOR="`echo '$(VERSION)' | sed -n -e "$$sed_extract_minor"` \
+ "-DPACKAGE_VERSION_SUBMINOR="`echo '$(VERSION)' | sed -n -e "$$sed_extract_subminor"` \
+ -i $(srcdir)/libintl.rc -o libintl.res --output-format=coff
+
+ref-add.sed: $(srcdir)/ref-add.sin
+ sed -e '/^#/d' -e 's/@''PACKAGE''@/@PACKAGE@/g' $(srcdir)/ref-add.sin > t-ref-add.sed
+ mv t-ref-add.sed ref-add.sed
+ref-del.sed: $(srcdir)/ref-del.sin
+ sed -e '/^#/d' -e 's/@''PACKAGE''@/@PACKAGE@/g' $(srcdir)/ref-del.sin > t-ref-del.sed
+ mv t-ref-del.sed ref-del.sed
+
+INCLUDES = -I. -I$(srcdir) -I..
+
+libgnuintl.h: $(srcdir)/libgnuintl.h.in
+ sed -e '/IN_LIBGLOCALE/d' \
+ -e 's,@''HAVE_POSIX_PRINTF''@, at HAVE_POSIX_PRINTF@,g' \
+ -e 's,@''HAVE_ASPRINTF''@, at HAVE_ASPRINTF@,g' \
+ -e 's,@''HAVE_SNPRINTF''@, at HAVE_SNPRINTF@,g' \
+ -e 's,@''HAVE_WPRINTF''@, at HAVE_WPRINTF@,g' \
+ < $(srcdir)/libgnuintl.h.in \
+ | if test '@WOE32DLL@' = yes; then \
+ sed -e 's/extern \([^()]*\);/extern __declspec (dllimport) \1;/'; \
+ else \
+ cat; \
+ fi \
+ | sed -e 's/extern \([^"]\)/extern LIBINTL_DLL_EXPORTED \1/' \
+ -e "/#define _LIBINTL_H/r $(srcdir)/export.h" \
+ | sed -e 's,@''HAVE_VISIBILITY''@, at HAVE_VISIBILITY@,g' \
+ > libgnuintl.h
+
+libintl.h: $(srcdir)/libgnuintl.h.in
+ sed -e '/IN_LIBGLOCALE/d' \
+ -e 's,@''HAVE_POSIX_PRINTF''@, at HAVE_POSIX_PRINTF@,g' \
+ -e 's,@''HAVE_ASPRINTF''@, at HAVE_ASPRINTF@,g' \
+ -e 's,@''HAVE_SNPRINTF''@, at HAVE_SNPRINTF@,g' \
+ -e 's,@''HAVE_WPRINTF''@, at HAVE_WPRINTF@,g' \
+ < $(srcdir)/libgnuintl.h.in > libintl.h
+
+charset.alias: $(srcdir)/config.charset
+ $(SHELL) $(srcdir)/config.charset '@host@' > t-$@
+ mv t-$@ $@
+
+check: all
+
+# We must not install the libintl.h/libintl.a files if we are on a
+# system which has the GNU gettext() function in its C library or in a
+# separate library.
+# If you want to use the one which comes with this version of the
+# package, you have to use `configure --with-included-gettext'.
+install: install-exec install-data
+install-exec: all
+ if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \
+ && test '@USE_INCLUDED_LIBINTL@' = yes; then \
+ $(mkdir_p) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir); \
+ $(INSTALL_DATA) libintl.h $(DESTDIR)$(includedir)/libintl.h; \
+ $(LIBTOOL) --mode=install \
+ $(INSTALL_DATA) libintl.$la $(DESTDIR)$(libdir)/libintl.$la; \
+ if test "@RELOCATABLE@" = yes; then \
+ dependencies=`sed -n -e 's,^dependency_libs=\(.*\),\1,p' < $(DESTDIR)$(libdir)/libintl.la | sed -e "s,^',," -e "s,'\$$,,"`; \
+ if test -n "$$dependencies"; then \
+ rm -f $(DESTDIR)$(libdir)/libintl.la; \
+ fi; \
+ fi; \
+ else \
+ : ; \
+ fi
+ if test "$(PACKAGE)" = "gettext-tools" \
+ && test '@USE_INCLUDED_LIBINTL@' = no \
+ && test @GLIBC2@ != no; then \
+ $(mkdir_p) $(DESTDIR)$(libdir); \
+ $(LIBTOOL) --mode=install \
+ $(INSTALL_DATA) libgnuintl.$la $(DESTDIR)$(libdir)/libgnuintl.$la; \
+ rm -f $(DESTDIR)$(libdir)/preloadable_libintl.so; \
+ $(INSTALL_DATA) $(DESTDIR)$(libdir)/libgnuintl.so $(DESTDIR)$(libdir)/preloadable_libintl.so; \
+ $(LIBTOOL) --mode=uninstall \
+ rm -f $(DESTDIR)$(libdir)/libgnuintl.$la; \
+ else \
+ : ; \
+ fi
+ if test '@USE_INCLUDED_LIBINTL@' = yes; then \
+ test @GLIBC21@ != no || $(mkdir_p) $(DESTDIR)$(libdir); \
+ temp=$(DESTDIR)$(libdir)/t-charset.alias; \
+ dest=$(DESTDIR)$(libdir)/charset.alias; \
+ if test -f $(DESTDIR)$(libdir)/charset.alias; then \
+ orig=$(DESTDIR)$(libdir)/charset.alias; \
+ sed -f ref-add.sed $$orig > $$temp; \
+ $(INSTALL_DATA) $$temp $$dest; \
+ rm -f $$temp; \
+ else \
+ if test @GLIBC21@ = no; then \
+ orig=charset.alias; \
+ sed -f ref-add.sed $$orig > $$temp; \
+ $(INSTALL_DATA) $$temp $$dest; \
+ rm -f $$temp; \
+ fi; \
+ fi; \
+ $(mkdir_p) $(DESTDIR)$(localedir); \
+ test -f $(DESTDIR)$(localedir)/locale.alias \
+ && orig=$(DESTDIR)$(localedir)/locale.alias \
+ || orig=$(srcdir)/locale.alias; \
+ temp=$(DESTDIR)$(localedir)/t-locale.alias; \
+ dest=$(DESTDIR)$(localedir)/locale.alias; \
+ sed -f ref-add.sed $$orig > $$temp; \
+ $(INSTALL_DATA) $$temp $$dest; \
+ rm -f $$temp; \
+ else \
+ : ; \
+ fi
+install-data: all
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
+ $(INSTALL_DATA) VERSION $(DESTDIR)$(gettextsrcdir)/VERSION; \
+ $(INSTALL_DATA) ChangeLog.inst $(DESTDIR)$(gettextsrcdir)/ChangeLog; \
+ dists="COPYING.LIB-2.0 COPYING.LIB-2.1 $(DISTFILES.common)"; \
+ for file in $$dists; do \
+ $(INSTALL_DATA) $(srcdir)/$$file \
+ $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ chmod a+x $(DESTDIR)$(gettextsrcdir)/config.charset; \
+ dists="$(DISTFILES.generated)"; \
+ for file in $$dists; do \
+ if test -f $$file; then dir=.; else dir=$(srcdir); fi; \
+ $(INSTALL_DATA) $$dir/$$file \
+ $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ dists="$(DISTFILES.obsolete)"; \
+ for file in $$dists; do \
+ rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ else \
+ : ; \
+ fi
+
+install-strip: install
+
+install-dvi install-html install-info install-ps install-pdf:
+
+installdirs:
+ if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \
+ && test '@USE_INCLUDED_LIBINTL@' = yes; then \
+ $(mkdir_p) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir); \
+ else \
+ : ; \
+ fi
+ if test "$(PACKAGE)" = "gettext-tools" \
+ && test '@USE_INCLUDED_LIBINTL@' = no \
+ && test @GLIBC2@ != no; then \
+ $(mkdir_p) $(DESTDIR)$(libdir); \
+ else \
+ : ; \
+ fi
+ if test '@USE_INCLUDED_LIBINTL@' = yes; then \
+ test @GLIBC21@ != no || $(mkdir_p) $(DESTDIR)$(libdir); \
+ $(mkdir_p) $(DESTDIR)$(localedir); \
+ else \
+ : ; \
+ fi
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
+ else \
+ : ; \
+ fi
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall:
+ if { test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; } \
+ && test '@USE_INCLUDED_LIBINTL@' = yes; then \
+ rm -f $(DESTDIR)$(includedir)/libintl.h; \
+ $(LIBTOOL) --mode=uninstall \
+ rm -f $(DESTDIR)$(libdir)/libintl.$la; \
+ else \
+ : ; \
+ fi
+ if test "$(PACKAGE)" = "gettext-tools" \
+ && test '@USE_INCLUDED_LIBINTL@' = no \
+ && test @GLIBC2@ != no; then \
+ rm -f $(DESTDIR)$(libdir)/preloadable_libintl.so; \
+ else \
+ : ; \
+ fi
+ if test '@USE_INCLUDED_LIBINTL@' = yes; then \
+ if test -f $(DESTDIR)$(libdir)/charset.alias; then \
+ temp=$(DESTDIR)$(libdir)/t-charset.alias; \
+ dest=$(DESTDIR)$(libdir)/charset.alias; \
+ sed -f ref-del.sed $$dest > $$temp; \
+ if grep '^# Packages using this file: $$' $$temp > /dev/null; then \
+ rm -f $$dest; \
+ else \
+ $(INSTALL_DATA) $$temp $$dest; \
+ fi; \
+ rm -f $$temp; \
+ fi; \
+ if test -f $(DESTDIR)$(localedir)/locale.alias; then \
+ temp=$(DESTDIR)$(localedir)/t-locale.alias; \
+ dest=$(DESTDIR)$(localedir)/locale.alias; \
+ sed -f ref-del.sed $$dest > $$temp; \
+ if grep '^# Packages using this file: $$' $$temp > /dev/null; then \
+ rm -f $$dest; \
+ else \
+ $(INSTALL_DATA) $$temp $$dest; \
+ fi; \
+ rm -f $$temp; \
+ fi; \
+ else \
+ : ; \
+ fi
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ for file in VERSION ChangeLog COPYING.LIB-2.0 COPYING.LIB-2.1 $(DISTFILES.common) $(DISTFILES.generated); do \
+ rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ else \
+ : ; \
+ fi
+
+info dvi ps pdf html:
+
+$(OBJECTS): ../config.h libgnuintl.h
+bindtextdom.$lo dcgettext.$lo dcigettext.$lo dcngettext.$lo dgettext.$lo dngettext.$lo finddomain.$lo gettext.$lo intl-compat.$lo loadmsgcat.$lo localealias.$lo ngettext.$lo textdomain.$lo: $(srcdir)/gettextP.h $(srcdir)/gmo.h $(srcdir)/loadinfo.h
+hash-string.$lo dcigettext.$lo loadmsgcat.$lo: $(srcdir)/hash-string.h
+explodename.$lo l10nflist.$lo: $(srcdir)/loadinfo.h
+dcigettext.$lo loadmsgcat.$lo plural.$lo plural-exp.$lo: $(srcdir)/plural-exp.h
+dcigettext.$lo: $(srcdir)/eval-plural.h
+localcharset.$lo: $(srcdir)/localcharset.h
+bindtextdom.$lo dcigettext.$lo finddomain.$lo loadmsgcat.$lo localealias.$lo lock.$lo log.$lo: $(srcdir)/lock.h
+localealias.$lo localcharset.$lo relocatable.$lo: $(srcdir)/relocatable.h
+printf.$lo: $(srcdir)/printf-args.h $(srcdir)/printf-args.c $(srcdir)/printf-parse.h $(srcdir)/wprintf-parse.h $(srcdir)/xsize.h $(srcdir)/printf-parse.c $(srcdir)/vasnprintf.h $(srcdir)/vasnwprintf.h $(srcdir)/vasnprintf.c
+
+# A bison-2.1 generated plural.c includes <libintl.h> if ENABLE_NLS.
+PLURAL_DEPS_yes = libintl.h
+PLURAL_DEPS_no =
+plural.$lo: $(PLURAL_DEPS_ at USE_INCLUDED_LIBINTL@)
+
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES)
+ here=`pwd`; cd $(srcdir) && etags -o $$here/TAGS $(HEADERS) $(SOURCES)
+
+ctags: CTAGS
+
+CTAGS: $(HEADERS) $(SOURCES)
+ here=`pwd`; cd $(srcdir) && ctags -o $$here/CTAGS $(HEADERS) $(SOURCES)
+
+id: ID
+
+ID: $(HEADERS) $(SOURCES)
+ here=`pwd`; cd $(srcdir) && mkid -f$$here/ID $(HEADERS) $(SOURCES)
+
+
+mostlyclean:
+ rm -f *.a *.la *.o *.obj *.lo libintl.res core core.*
+ rm -f libgnuintl.h libintl.h charset.alias ref-add.sed ref-del.sed
+ rm -f -r .libs _libs
+
+clean: mostlyclean
+
+distclean: clean
+ rm -f Makefile ID TAGS
+ if test "$(PACKAGE)" = "gettext-runtime" || test "$(PACKAGE)" = "gettext-tools"; then \
+ rm -f ChangeLog.inst $(DISTFILES.normal); \
+ else \
+ : ; \
+ fi
+
+maintainer-clean: distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+
+# GNU gettext needs not contain the file `VERSION' but contains some
+# other files which should not be distributed in other packages.
+distdir = ../$(PACKAGE)-$(VERSION)/$(subdir)
+dist distdir: Makefile
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ : ; \
+ else \
+ if test "$(PACKAGE)" = "gettext-runtime"; then \
+ additional="$(DISTFILES.gettext)"; \
+ else \
+ additional="$(DISTFILES.normal)"; \
+ fi; \
+ $(MAKE) $(DISTFILES.common) $(DISTFILES.generated) $$additional; \
+ for file in ChangeLog $(DISTFILES.common) $(DISTFILES.generated) $$additional; do \
+ if test -f $$file; then dir=.; else dir=$(srcdir); fi; \
+ cp -p $$dir/$$file $(distdir) || test $$file = Makefile.in || exit 1; \
+ done; \
+ fi
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status
+# This would be more efficient, but doesn't work any more with autoconf-2.57,
+# when AC_CONFIG_FILES([intl/Makefile:somedir/Makefile.in]) is used.
+# cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Property changes on: branches/commonification/tuxmath/trunk/linebreak
___________________________________________________________________
Modified: svn:ignore
- *~
*.o
.deps
CMakeFiles
Makefile
Makefile.in
+ *~
*.o
Makefile
Makefile.in
Property changes on: branches/commonification/tuxmath/trunk/src
___________________________________________________________________
Modified: svn:ignore
- *~
*.o
.deps
CMakeFiles
Makefile
Makefile.in
tuxmath
tuxmathadmin
generate_lesson
+ *~
*.o
Makefile
Makefile.in
tuxmath
tuxmathadmin
generate-lesson
Modified: branches/commonification/tuxmath/trunk/src/CMakeLists.txt
===================================================================
--- branches/commonification/tuxmath/trunk/src/CMakeLists.txt 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/CMakeLists.txt 2009-11-24 23:54:32 UTC (rev 1674)
@@ -7,6 +7,7 @@
find_package(SDL_ttf REQUIRED)
find_package(SDL_mixer REQUIRED)
find_package(SDL_gfx)
+find_package(SDL_net)
find_package(t4kcommon)
if (NOT SDLGFX_FOUND)
@@ -14,7 +15,9 @@
set(TUXMATH_EXTRA_SRC ${TUXMATH_EXTRA_SRC} SDL_rotozoom.c)
endif (NOT SDLGFX_FOUND)
+set (HAVE_LIBT4KCOMMON ${T4KCOMMON_FOUND})
+
## Define the source files used for each executable
# tuxmath
set(SOURCES_TUXMATH
@@ -30,11 +33,13 @@
lessons.c
loaders.c
mathcards.c
+ network.c
options.c
pixels.c
scandir.c
SDL_extras.c
setup.c
+ throttle.c
titlescreen.c
multiplayer.c
campaign.c
@@ -71,6 +76,9 @@
if (SDLGFX_FOUND)
include_directories(${SDLGFX_INCLUDE_DIR})
endif (SDLGFX_FOUND)
+if (T4KCOMMON_FOUND)
+ include_directories(${T4KCOMMON_INCLUDE_DIR})
+endif (T4KCOMMON_FOUND)
if (TUXMATH_BUILD_INTL)
link_directories(${INTL_BINARY_DIR})
@@ -126,6 +134,7 @@
${SDLIMAGE_LIBRARY}
${SDLTTF_LIBRARY}
${SDLMIXER_LIBRARY}
+ ${SDLNET_LIBRARY}
)
if (SDLPANGO_FOUND)
Modified: branches/commonification/tuxmath/trunk/src/Makefile.am
===================================================================
--- branches/commonification/tuxmath/trunk/src/Makefile.am 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/Makefile.am 2009-11-24 23:54:32 UTC (rev 1674)
@@ -19,11 +19,16 @@
if BUILD_MINGW32
- bin_PROGRAMS = TuxMath
+ bin_PROGRAMS = TuxMath tuxmathserver
DATA_PREFIX=@MINGW32_PACKAGE_DATA_DIR@
else
TUXMATHRC =
- bin_PROGRAMS = tuxmath tuxmathadmin generate_lesson
+ bin_PROGRAMS = tuxmath \
+ tuxmathadmin \
+ generate_lesson \
+ tuxmathserver \
+ tuxmathtestclient
+
DATA_PREFIX=${pkgdatadir}
endif
@@ -31,6 +36,7 @@
tuxmath_SOURCES = tuxmath.c \
setup.c \
titlescreen.c \
+ menu.c \
game.c \
factoroids.c \
fileops_media.c \
@@ -40,7 +46,7 @@
linewrap.c \
loaders.c \
audio.c \
- menu.c \
+ network.c \
mathcards.c \
campaign.c \
multiplayer.c \
@@ -50,7 +56,9 @@
SDL_rotozoom.c \
lessons.c \
scandir.c \
- pixels.c
+ pixels.c \
+ server.c \
+ throttle.c
# HACK "TuxMath" is the Windows program, whereas "tuxmath" is the Unix program
@@ -64,6 +72,18 @@
fileops.c \
lessons.c
+tuxmathserver_SOURCES = servermain.c \
+ server.c \
+ mathcards.c \
+ throttle.c \
+ options.c
+
+tuxmathtestclient_SOURCES = testclient.c \
+ throttle.c \
+ network.c \
+ options.c \
+ mathcards.c
+
EXTRA_DIST = credits.h \
factoroids.h \
fileops.h \
@@ -73,10 +93,12 @@
highscore.h \
linewrap.h \
loaders.h \
- mathcards.h \
+ network.h \
+ titlescreen.h \
+ menu.h \
options.h \
setup.h \
- titlescreen.h \
+ mathcards.h \
campaign.h \
multiplayer.h \
tuxmath.h \
@@ -87,7 +109,12 @@
gettext.h \
scandir.h \
pixels.h \
- compiler.h
+ compiler.h \
+ server.h \
+ testclient.h \
+ transtruct.h \
+ throttle.h
+
WINDRES=@WINDRES@
@@ -95,7 +122,3 @@
# How to make an RC file
tuxmathrc.o: tuxmathrc.rc
$(WINDRES) -i $< -o $@
-
-
-
-
Modified: branches/commonification/tuxmath/trunk/src/SDL_extras.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/SDL_extras.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/SDL_extras.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1210,7 +1210,7 @@
/* "Public" functions called from other files that use either */
/*SDL_Pango or SDL_ttf: */
-
+#ifndef HAVE_LIBT4KCOMMON
/* For setup, we either initialize SDL_Pango and set its context, */
/* or we initialize SDL_ttf: */
int Setup_SDL_Text(void)
@@ -1274,27 +1274,27 @@
#ifdef HAVE_LIBSDL_PANGO
if (!context)
{
- fprintf(stderr, "BlackOutline(): invalid SDL_Pango context - returning.");
+ fprintf(stderr, "BlackOutline(): invalid SDL_Pango context - returning.\n");
return NULL;
}
#else
TTF_Font* font = get_font(size);
if (!font)
{
- fprintf(stderr, "BlackOutline(): could not load needed font - returning.");
+ fprintf(stderr, "BlackOutline(): could not load needed font - returning.\n");
return NULL;
}
#endif
if (!t || !c)
{
- fprintf(stderr, "BlackOutline(): invalid ptr parameter, returning.");
+ fprintf(stderr, "BlackOutline(): invalid ptr parameter, returning.\n");
return NULL;
}
if (t[0] == '\0')
{
- fprintf(stderr, "BlackOutline(): empty string, returning");
+ fprintf(stderr, "BlackOutline(): empty string, returning\n");
return NULL;
}
@@ -1475,9 +1475,9 @@
return surf;
}
+#endif //HAVE_LIBT4KCOMMON
-
/*-----------------------------------------------------------*/
/* Local functions, callable only within SDL_extras, divided */
/* according with which text lib we are using: */
Modified: branches/commonification/tuxmath/trunk/src/credits.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/credits.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/credits.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -26,7 +26,6 @@
#include "tuxmath.h"
#include "options.h"
#include "fileops.h"
-#include "fileops_media.h"
#include "setup.h"
#include "credits.h"
#include "SDL_extras.h"
Modified: branches/commonification/tuxmath/trunk/src/factoroids.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/factoroids.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/factoroids.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -34,7 +34,6 @@
#include "game.h"
#include "fileops.h"
-#include "fileops_media.h"
#include "setup.h"
#include "mathcards.h"
#include "loaders.h"
Modified: branches/commonification/tuxmath/trunk/src/fileops.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/fileops.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/fileops.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -16,8 +16,11 @@
#ifndef FILEOPS_H
#define FILEOPS_H
+#if HAVE_T4KCOMMON
+#include "t4kcommon.h"
+#endif
+
#include "globals.h"
-#include "SDL.h"
/* Flag basically telling whether or not to allow admin-level */
/* settings to be changed: */
@@ -26,6 +29,172 @@
GLOBAL_CONFIG_FILE
};
+/* Names for images (formerly in images.h) */
+enum {
+ IMG_TITLE,
+ IMG_LEFT,
+ IMG_LEFT_GRAY,
+ IMG_RIGHT,
+ IMG_RIGHT_GRAY,
+ IMG_TUX4KIDS,
+ IMG_NBS,
+ IMG_CITY_BLUE,
+ IMG_CITY_BLUE_EXPL1,
+ IMG_CITY_BLUE_EXPL2,
+ IMG_CITY_BLUE_EXPL3,
+ IMG_CITY_BLUE_EXPL4,
+ IMG_CITY_BLUE_EXPL5,
+ IMG_CITY_BLUE_DEAD,
+ IMG_CITY_GREEN,
+ IMG_CITY_GREEN_EXPL1,
+ IMG_CITY_GREEN_EXPL2,
+ IMG_CITY_GREEN_EXPL3,
+ IMG_CITY_GREEN_EXPL4,
+ IMG_CITY_GREEN_EXPL5,
+ IMG_CITY_GREEN_DEAD,
+ IMG_CITY_ORANGE,
+ IMG_CITY_ORANGE_EXPL1,
+ IMG_CITY_ORANGE_EXPL2,
+ IMG_CITY_ORANGE_EXPL3,
+ IMG_CITY_ORANGE_EXPL4,
+ IMG_CITY_ORANGE_EXPL5,
+ IMG_CITY_ORANGE_DEAD,
+ IMG_CITY_RED,
+ IMG_CITY_RED_EXPL1,
+ IMG_CITY_RED_EXPL2,
+ IMG_CITY_RED_EXPL3,
+ IMG_CITY_RED_EXPL4,
+ IMG_CITY_RED_EXPL5,
+ IMG_CITY_RED_DEAD,
+ IMG_SHIELDS,
+ IMG_MINI_COMET1,
+ IMG_MINI_COMET2,
+ IMG_MINI_COMET3,
+ IMG_NUMS,
+ IMG_LEDNUMS,
+ IMG_LED_NEG_SIGN,
+ IMG_PAUSED,
+ IMG_DEMO,
+ IMG_DEMO_SMALL,
+ IMG_KEYPAD,
+ IMG_KEYPAD_NO_NEG,
+ IMG_CONSOLE_LED,
+ IMG_CONSOLE_BASH,
+ IMG_TUX_CONSOLE1,
+ IMG_TUX_CONSOLE2,
+ IMG_TUX_CONSOLE3,
+ IMG_TUX_CONSOLE4,
+ IMG_TUX_RELAX1,
+ IMG_TUX_RELAX2,
+ IMG_TUX_EGYPT1,
+ IMG_TUX_EGYPT2,
+ IMG_TUX_EGYPT3,
+ IMG_TUX_EGYPT4,
+ IMG_TUX_RELAX,
+ IMG_TUX_ALARM,
+ IMG_TUX_DRAT,
+ IMG_TUX_YIPE,
+ IMG_TUX_YAY1,
+ IMG_TUX_YAY2,
+ IMG_TUX_YES1,
+ IMG_TUX_YES2,
+ IMG_TUX_SIT,
+ IMG_TUX_FIST1,
+ IMG_TUX_FIST2,
+ IMG_PENGUIN_FLAPDOWN,
+ IMG_PENGUIN_FLAPUP,
+ IMG_PENGUIN_INCOMING,
+ IMG_PENGUIN_GRUMPY,
+ IMG_PENGUIN_WORRIED,
+ IMG_PENGUIN_STANDING_UP,
+ IMG_PENGUIN_SITTING_DOWN,
+ IMG_PENGUIN_WALK_ON1,
+ IMG_PENGUIN_WALK_ON2,
+ IMG_PENGUIN_WALK_ON3,
+ IMG_PENGUIN_WALK_OFF1,
+ IMG_PENGUIN_WALK_OFF2,
+ IMG_PENGUIN_WALK_OFF3,
+ IMG_IGLOO_MELTED3,
+ IMG_IGLOO_MELTED2,
+ IMG_IGLOO_MELTED1,
+ IMG_IGLOO_HALF,
+ IMG_IGLOO_INTACT,
+ IMG_IGLOO_REBUILDING1,
+ IMG_IGLOO_REBUILDING2,
+ IMG_STEAM1,
+ IMG_STEAM2,
+ IMG_STEAM3,
+ IMG_STEAM4,
+ IMG_STEAM5,
+ IMG_CLOUD,
+ IMG_SNOW1,
+ IMG_SNOW2,
+ IMG_SNOW3,
+ IMG_EXTRA_LIFE,
+ IMG_WAVE,
+ IMG_SCORE,
+ IMG_STOP,
+ IMG_NUMBERS,
+ IMG_GAMEOVER,
+ IMG_GAMEOVER_WON,
+ BG_STARS,
+ IMG_ASTEROID1,
+ IMG_ASTEROID2,
+ IMG_ASTEROID3,
+ IMG_SHIP01,
+ IMG_FACTOROIDS,
+ IMG_FACTORS,
+ IMG_TUX_LITTLE,
+ IMG_GOOD,
+ NUM_IMAGES
+};
+
+/* Names for animated images (sprites) */
+enum {
+ IMG_COMET,
+ IMG_BONUS_COMET,
+ IMG_COMET_EXPL,
+ IMG_BONUS_COMET_EXPL,
+ ANIM_COMET,
+ ANIM_BONUS_COMET,
+ ANIM_COMET_EXPL,
+ ANIM_BONUS_COMET_EXPL,
+ ANIM_COMET_MINI,
+ ANIM_TUX_CONSOLE,
+ ANIM_TUX_EGYPT_LEFT,
+ ANIM_TUX_EGYPT_RIGHT,
+ ANIM_TUX_YAY,
+ ANIM_TUX_YES,
+ ANIM_TUX_FIST,
+ ANIM_STEAM,
+ NUM_SPRITES
+};
+
+/* Names for game sounds (formerly in sounds.h): */
+enum {
+ SND_HARP,
+ SND_POP,
+ SND_TOCK,
+ SND_LASER,
+ SND_BUZZ,
+ SND_ALARM,
+ SND_SHIELDSDOWN,
+ SND_EXPLOSION,
+ SND_CLICK,
+ SND_SIZZLE,
+ SND_BONUS_COMET,
+ SND_EXTRA_LIFE,
+ NUM_SOUNDS
+};
+
+/* Names for background music (also formerly in sounds.h): */
+enum {
+ MUS_GAME,
+ MUS_GAME2,
+ MUS_GAME3,
+ NUM_MUSICS
+};
+
/* Names for game summary files: */
enum {
SUMMARY1,
@@ -64,4 +233,15 @@
/* These functions used by game() to record game summary: */
int write_pregame_summary(void);
int write_postgame_summary(void);
+
+int load_image_data();
+int SetImage(int id, SDL_Rect* rect);
+SDL_Surface* GetImage(int id);
+sprite* GetSprite(int id);
+
+
+#ifndef NOSOUND
+int load_sound_data();
#endif
+
+#endif
Modified: branches/commonification/tuxmath/trunk/src/fileops_media.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/fileops_media.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/fileops_media.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,5 +1,5 @@
#include "tuxmath.h"
-#include "fileops_media.h"
+#include "fileops.h"
#include "loaders.h"
#include "options.h"
#include "SDL_extras.h"
@@ -17,135 +17,138 @@
/* TODO load only "igloo" or "city" files, not both. */
/* TODO get rid of files no longer used. */
-static char fn[PATH_MAX];
+int load_image_data()
+{
+ int i;
-static char* image_filenames[NUM_IMAGES] = {
- "status/title.svg",
- "status/left.svg",
- "status/left_gray.svg",
- "status/right.svg",
- "status/right_gray.svg",
- "status/tux4kids.svg",
- "status/nbs.svg",
- "cities/city-blue.svg",
- "cities/csplode-blue-1.svg",
- "cities/csplode-blue-2.svg",
- "cities/csplode-blue-3.svg",
- "cities/csplode-blue-4.svg",
- "cities/csplode-blue-5.svg",
- "cities/cdead-blue.svg",
- "cities/city-green.svg",
- "cities/csplode-green-1.svg",
- "cities/csplode-green-2.svg",
- "cities/csplode-green-3.svg",
- "cities/csplode-green-4.svg",
- "cities/csplode-green-5.svg",
- "cities/cdead-green.svg",
- "cities/city-orange.svg",
- "cities/csplode-orange-1.svg",
- "cities/csplode-orange-2.svg",
- "cities/csplode-orange-3.svg",
- "cities/csplode-orange-4.svg",
- "cities/csplode-orange-5.svg",
- "cities/cdead-orange.svg",
- "cities/city-red.svg",
- "cities/csplode-red-1.svg",
- "cities/csplode-red-2.svg",
- "cities/csplode-red-3.svg",
- "cities/csplode-red-4.svg",
- "cities/csplode-red-5.svg",
- "cities/cdead-red.svg",
- "cities/shields.svg",
- "status/nums.svg",
- "status/lednums.svg",
- "status/led_neg_sign.svg",
- "status/paused.svg",
- "status/demo.svg",
- "status/demo-small.svg",
- "status/keypad.svg",
- "status/keypad_no_neg.svg",
- "tux/console_led.svg",
- "tux/console_bash.svg",
- "tux/tux-relax.svg",
- "tux/tux-alarm.svg",
- "tux/tux-drat.svg",
- "tux/tux-yipe.svg",
- "tux/tux-sit.svg",
- "penguins/flapdown.svg",
- "penguins/flapup.svg",
- "penguins/incoming.svg",
- "penguins/grumpy.svg",
- "penguins/worried.svg",
- "penguins/standing-up.svg",
- "penguins/sitting-down.svg",
- "penguins/walk-on1.svg",
- "penguins/walk-on2.svg",
- "penguins/walk-on3.svg",
- "penguins/walk-off1.svg",
- "penguins/walk-off2.svg",
- "penguins/walk-off3.svg",
- "igloos/melted3.svg",
- "igloos/melted2.svg",
- "igloos/melted1.svg",
- "igloos/half.svg",
- "igloos/intact.svg",
- "igloos/rebuilding1.svg",
- "igloos/rebuilding2.svg",
- "igloos/cloud.svg",
- "igloos/snow1.svg",
- "igloos/snow2.svg",
- "igloos/snow3.svg",
- "igloos/extra_life.svg",
- "status/wave.svg",
- "status/score.svg",
- "status/stop.svg",
- "status/numbers.svg",
- "status/gameover.svg",
- "status/gameover_won.svg",
- "factoroids/gbstars.svg",
- "factoroids/asteroid1.svg",
- "factoroids/asteroid2.svg",
- "factoroids/asteroid3.svg",
- "factoroids/ship01.svg",
- "factoroids/factoroids.svg",
- "factoroids/factors.svg",
- "factoroids/tux.svg",
- "factoroids/good.svg"
+ static char* image_filenames[NUM_IMAGES] = {
+ "status/title.png",
+ "status/left.png",
+ "status/left_gray.png",
+ "status/right.png",
+ "status/right_gray.png",
+ "status/tux4kids.png",
+ "status/nbs.png",
+ "cities/city-blue.png",
+ "cities/csplode-blue-1.png",
+ "cities/csplode-blue-2.png",
+ "cities/csplode-blue-3.png",
+ "cities/csplode-blue-4.png",
+ "cities/csplode-blue-5.png",
+ "cities/cdead-blue.png",
+ "cities/city-green.png",
+ "cities/csplode-green-1.png",
+ "cities/csplode-green-2.png",
+ "cities/csplode-green-3.png",
+ "cities/csplode-green-4.png",
+ "cities/csplode-green-5.png",
+ "cities/cdead-green.png",
+ "cities/city-orange.png",
+ "cities/csplode-orange-1.png",
+ "cities/csplode-orange-2.png",
+ "cities/csplode-orange-3.png",
+ "cities/csplode-orange-4.png",
+ "cities/csplode-orange-5.png",
+ "cities/cdead-orange.png",
+ "cities/city-red.png",
+ "cities/csplode-red-1.png",
+ "cities/csplode-red-2.png",
+ "cities/csplode-red-3.png",
+ "cities/csplode-red-4.png",
+ "cities/csplode-red-5.png",
+ "cities/cdead-red.png",
+ "cities/shields.png",
+ "comets/mini_comet1.png",
+ "comets/mini_comet2.png",
+ "comets/mini_comet3.png",
+ "status/nums.png",
+ "status/lednums.png",
+ "status/led_neg_sign.png",
+ "status/paused.png",
+ "status/demo.png",
+ "status/demo-small.png",
+ "status/keypad.png",
+ "status/keypad_no_neg.png",
+ "tux/console_led.png",
+ "tux/console_bash.png",
+ "tux/tux-console1.png",
+ "tux/tux-console2.png",
+ "tux/tux-console3.png",
+ "tux/tux-console4.png",
+ "tux/tux-relax1.png",
+ "tux/tux-relax2.png",
+ "tux/tux-egypt1.png",
+ "tux/tux-egypt2.png",
+ "tux/tux-egypt3.png",
+ "tux/tux-egypt4.png",
+ "tux/tux-drat.png",
+ "tux/tux-yipe.png",
+ "tux/tux-yay1.png",
+ "tux/tux-yay2.png",
+ "tux/tux-yes1.png",
+ "tux/tux-yes2.png",
+ "tux/tux-sit.png",
+ "tux/tux-fist1.png",
+ "tux/tux-fist2.png",
+ "penguins/flapdown.png",
+ "penguins/flapup.png",
+ "penguins/incoming.png",
+ "penguins/grumpy.png",
+ "penguins/worried.png",
+ "penguins/standing-up.png",
+ "penguins/sitting-down.png",
+ "penguins/walk-on1.png",
+ "penguins/walk-on2.png",
+ "penguins/walk-on3.png",
+ "penguins/walk-off1.png",
+ "penguins/walk-off2.png",
+ "penguins/walk-off3.png",
+ "igloos/melted3.png",
+ "igloos/melted2.png",
+ "igloos/melted1.png",
+ "igloos/half.png",
+ "igloos/intact.png",
+ "igloos/rebuilding1.png",
+ "igloos/rebuilding2.png",
+ "igloos/steam1.png",
+ "igloos/steam2.png",
+ "igloos/steam3.png",
+ "igloos/steam4.png",
+ "igloos/steam5.png",
+ "igloos/cloud.png",
+ "igloos/snow1.png",
+ "igloos/snow2.png",
+ "igloos/snow3.png",
+ "igloos/extra_life.png",
+ "status/wave.png",
+ "status/score.png",
+ "status/stop.png",
+ "status/numbers.png",
+ "status/gameover.png",
+ "status/gameover_won.png",
+ "factoroids/gbstars.png",
+ "factoroids/asteroid1.png",
+ "factoroids/asteroid2.png",
+ "factoroids/asteroid3.png",
+ "factoroids/ship01.png",
+ "factoroids/factoroids.png",
+ "factoroids/factors.png",
+ "factoroids/tux.png",
+ "factoroids/good.png"
};
-static char* sprite_filenames[NUM_IMAGES] = {
+ static char* sprite_filenames[NUM_IMAGES] = {
"comets/comet",
"comets/bonus_comet",
"comets/cometex",
- "comets/bonus_cometex",
- "comets/mini_comet",
- "tux/tux-console",
- "tux/tux-egypt-left",
- "tux/tux-egypt-right",
- "tux/tux-yay",
- "tux/tux-yes",
- "tux/tux-fist",
- "igloos/steam"
+ "comets/bonus_cometex"
};
-
-int load_image_data()
-{
- int i;
-
/* Load static images: */
for (i = 0; i < NUM_IMAGES; i++)
{
- if(images[i])
- {
- DEBUGMSG(debug_loaders, "load_image_data(): file %s already loaded\n",
- image_filenames[i]);
- continue;
- }
+ images[i] = LoadImage(image_filenames[i], IMG_ALPHA);
- sprintf(fn, "%s%s%s", DATA_PREFIX, "/images/", image_filenames[i]);
- images[i] = LoadImage(fn, IMG_ALPHA);
-
if (images[i] == NULL)
{
fprintf(stderr,
@@ -160,8 +163,7 @@
/* Load animated graphics: */
for (i = 0; i < NUM_SPRITES; i++)
{
- sprintf(fn, "%s%s%s", DATA_PREFIX, "/images/", sprite_filenames[i]);
- sprites[i] = LoadSprite(fn, IMG_ALPHA);
+ sprites[i] = LoadSprite(sprite_filenames[i], IMG_ALPHA);
if (sprites[i] == NULL)
{
@@ -174,7 +176,6 @@
}
}
-
glyph_offset = 0;
#ifdef REPLACE_WAVESCORE
@@ -190,41 +191,10 @@
return 1;
}
-SDL_Surface* GetImage(int id)
-{
- return images[id];
-}
-sprite* GetSprite(int id)
-{
- return sprites[id];
-}
-int SetImage(int id, SDL_Rect* rect)
-{
- if(images[id])
- SDL_FreeSurface(images[id]);
- sprintf(fn, "%s%s%s", DATA_PREFIX, "/images/", image_filenames[id]);
- if(rect)
- images[id] = LoadImageOfBoundingBox(fn, IMG_ALPHA, rect->w, rect->h);
- else
- images[id] = LoadImage(fn, IMG_ALPHA);
- if(NULL == images[id])
- {
- fprintf(stderr,
- "\nError: I couldn't load a graphics file:\n"
- "%s\n"
- "The Simple DirectMedia error that occured was:\n"
- "%s\n\n", image_filenames[id], SDL_GetError());
- return 0;
- }
- return 1;
-}
-
-
-
#ifndef NOSOUND
int load_sound_data(void)
{
@@ -239,7 +209,6 @@
DATA_PREFIX "/sounds/alarm.wav",
DATA_PREFIX "/sounds/shieldsdown.wav",
DATA_PREFIX "/sounds/explosion.wav",
- DATA_PREFIX "/sounds/click.wav",
DATA_PREFIX "/sounds/sizzling.wav",
DATA_PREFIX "/sounds/towerclock.wav",
DATA_PREFIX "/sounds/cheer.wav"
Deleted: branches/commonification/tuxmath/trunk/src/fileops_media.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/fileops_media.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/fileops_media.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,156 +0,0 @@
-#ifndef FILEOPS_MEDIA_H
-#define FILEOPS_MEDIA_H
-
-#include "globals.h"
-#include "SDL.h"
-
-/* Names for images (formerly in images.h) */
-enum {
- IMG_TITLE,
- IMG_LEFT,
- IMG_LEFT_GRAY,
- IMG_RIGHT,
- IMG_RIGHT_GRAY,
- IMG_TUX4KIDS,
- IMG_NBS,
- IMG_CITY_BLUE,
- IMG_CITY_BLUE_EXPL1,
- IMG_CITY_BLUE_EXPL2,
- IMG_CITY_BLUE_EXPL3,
- IMG_CITY_BLUE_EXPL4,
- IMG_CITY_BLUE_EXPL5,
- IMG_CITY_BLUE_DEAD,
- IMG_CITY_GREEN,
- IMG_CITY_GREEN_EXPL1,
- IMG_CITY_GREEN_EXPL2,
- IMG_CITY_GREEN_EXPL3,
- IMG_CITY_GREEN_EXPL4,
- IMG_CITY_GREEN_EXPL5,
- IMG_CITY_GREEN_DEAD,
- IMG_CITY_ORANGE,
- IMG_CITY_ORANGE_EXPL1,
- IMG_CITY_ORANGE_EXPL2,
- IMG_CITY_ORANGE_EXPL3,
- IMG_CITY_ORANGE_EXPL4,
- IMG_CITY_ORANGE_EXPL5,
- IMG_CITY_ORANGE_DEAD,
- IMG_CITY_RED,
- IMG_CITY_RED_EXPL1,
- IMG_CITY_RED_EXPL2,
- IMG_CITY_RED_EXPL3,
- IMG_CITY_RED_EXPL4,
- IMG_CITY_RED_EXPL5,
- IMG_CITY_RED_DEAD,
- IMG_SHIELDS,
- IMG_NUMS,
- IMG_LEDNUMS,
- IMG_LED_NEG_SIGN,
- IMG_PAUSED,
- IMG_DEMO,
- IMG_DEMO_SMALL,
- IMG_KEYPAD,
- IMG_KEYPAD_NO_NEG,
- IMG_CONSOLE_LED,
- IMG_CONSOLE_BASH,
- IMG_TUX_RELAX,
- IMG_TUX_ALARM,
- IMG_TUX_DRAT,
- IMG_TUX_YIPE,
- IMG_TUX_SIT,
- IMG_PENGUIN_FLAPDOWN,
- IMG_PENGUIN_FLAPUP,
- IMG_PENGUIN_INCOMING,
- IMG_PENGUIN_GRUMPY,
- IMG_PENGUIN_WORRIED,
- IMG_PENGUIN_STANDING_UP,
- IMG_PENGUIN_SITTING_DOWN,
- IMG_PENGUIN_WALK_ON1,
- IMG_PENGUIN_WALK_ON2,
- IMG_PENGUIN_WALK_ON3,
- IMG_PENGUIN_WALK_OFF1,
- IMG_PENGUIN_WALK_OFF2,
- IMG_PENGUIN_WALK_OFF3,
- IMG_IGLOO_MELTED3,
- IMG_IGLOO_MELTED2,
- IMG_IGLOO_MELTED1,
- IMG_IGLOO_HALF,
- IMG_IGLOO_INTACT,
- IMG_IGLOO_REBUILDING1,
- IMG_IGLOO_REBUILDING2,
- IMG_CLOUD,
- IMG_SNOW1,
- IMG_SNOW2,
- IMG_SNOW3,
- IMG_EXTRA_LIFE,
- IMG_WAVE,
- IMG_SCORE,
- IMG_STOP,
- IMG_NUMBERS,
- IMG_GAMEOVER,
- IMG_GAMEOVER_WON,
- BG_STARS,
- IMG_ASTEROID1,
- IMG_ASTEROID2,
- IMG_ASTEROID3,
- IMG_SHIP01,
- IMG_FACTOROIDS,
- IMG_FACTORS,
- IMG_TUX_LITTLE,
- IMG_GOOD,
- NUM_IMAGES
-};
-
-/* Names for animated images (sprites) */
-enum {
- ANIM_COMET,
- ANIM_BONUS_COMET,
- ANIM_COMET_EXPL,
- ANIM_BONUS_COMET_EXPL,
- ANIM_COMET_MINI,
- ANIM_TUX_CONSOLE,
- ANIM_TUX_EGYPT_LEFT,
- ANIM_TUX_EGYPT_RIGHT,
- ANIM_TUX_YAY,
- ANIM_TUX_YES,
- ANIM_TUX_FIST,
- ANIM_STEAM,
- NUM_SPRITES
-};
-
-/* Names for game sounds (formerly in sounds.h): */
-enum {
- SND_HARP,
- SND_POP,
- SND_TOCK,
- SND_LASER,
- SND_BUZZ,
- SND_ALARM,
- SND_SHIELDSDOWN,
- SND_EXPLOSION,
- SND_CLICK,
- SND_SIZZLE,
- SND_BONUS_COMET,
- SND_EXTRA_LIFE,
- NUM_SOUNDS
-};
-
-/* Names for background music (also formerly in sounds.h): */
-enum {
- MUS_GAME,
- MUS_GAME2,
- MUS_GAME3,
- NUM_MUSICS
-};
-
-
-int load_image_data();
-int SetImage(int id, SDL_Rect* rect);
-SDL_Surface* GetImage(int id);
-sprite* GetSprite(int id);
-
-#ifndef NOSOUND
-int load_sound_data();
-#endif
-
-
-#endif
Modified: branches/commonification/tuxmath/trunk/src/game.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/game.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/game.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -15,7 +15,7 @@
August 26, 2001 - February 18, 2004
Revised by David Bruce, Tim Holy and others
- 2005-2007
+ 2005-2009
*/
/* put this first so we get <config.h> and <gettext.h> immediately: */
@@ -26,14 +26,21 @@
#include <string.h>
#include "SDL.h"
+#include "SDL_image.h"
+
#ifndef NOSOUND
#include "SDL_mixer.h"
#endif
-#include "SDL_image.h"
+/* Make sure we don't try to call network code if we built without */
+/* network support: */
+#ifdef HAVE_LIBSDL_NET
+#include "network.h"
+#endif
+
+#include "transtruct.h"
#include "game.h"
#include "fileops.h"
-#include "fileops_media.h"
#include "setup.h"
#include "loaders.h"
#include "mathcards.h"
@@ -41,7 +48,10 @@
#include "titlescreen.h"
#include "options.h"
#include "SDL_extras.h"
+#include "pixels.h"
+#include "throttle.h"
+
#define FPS 15 /* 15 frames per second */
#define MS_PER_FRAME (1000 / FPS)
@@ -96,6 +106,7 @@
static int gameover_counter;
static int game_status;
static int user_quit_received;
+static int total_questions_left;
static int paused;
static int wave;
static int score;
@@ -107,7 +118,6 @@
static int demo_countdown;
static int tux_anim_frame;
static int num_cities_alive;
-static int num_comets_alive;
static int frame;
static int neg_answer_picked;
static int tux_pressing;
@@ -119,6 +129,8 @@
static int bonus_comet_counter;
static int extra_life_earned;
static int key_pressed;
+static int game_over_other;
+static int game_over_won;
static SDL_Surface* tux_img = NULL;
static SDL_Surface* old_tux_img = NULL;
@@ -148,6 +160,14 @@
static game_message s1, s2, s3, s4, s5;
static int start_message_chosen = 0;
+/*****************************************************************/
+MC_FlashCard quest_queue[QUEST_QUEUE_SIZE]; //current questions
+int remaining_quests = 0;
+static int comet_counter = 0;
+static int lan_players = 0;
+char lan_pnames[MAX_CLIENTS][NAME_SIZE];
+int lan_pscores[MAX_CLIENTS];
+/****************************************************************/
typedef struct {
int x_is_blinking;
@@ -179,10 +199,10 @@
static int check_extra_life(void);
static int check_exit_conditions(void);
-static void game_set_message(game_message *,const char *,int x, int y);
+static void game_set_message(game_message*, const char* ,int x, int y);
static void game_clear_message(game_message*);
static void game_clear_messages(void);
-static void game_write_message(const game_message* msg);
+void game_write_message(const game_message* msg);
static void game_write_messages(void);
static void draw_led_console(void);
static void draw_question_counter(void);
@@ -192,6 +212,7 @@
static int add_comet(void);
static void add_score(int inc);
static void reset_comets(void);
+static int num_comets_alive(void);
static void game_mouse_event(SDL_Event event);
static void game_key_event(SDLKey key);
@@ -203,75 +224,64 @@
void putpixel(SDL_Surface* surface, int x, int y, Uint32 pixel);
+/*****************************************************/
+#ifdef HAVE_LIBSDL_NET
+void game_handle_net_messages(void);
+void game_handle_net_msg(char* buf);
+int add_quest_recvd(char* buf);
+int remove_quest_recvd(char* buf);
+int connected_players_recvd(char* buf);
+int update_score_recvd(char* buf);
+int erase_comet_on_screen(comet_type* comet_ques);
+void print_current_quests(void);
+MC_FlashCard* search_queue_by_id(int id);
+comet_type* search_comets_by_id(int id);
+/******************************************************/
+#endif
+
+
static void print_exit_conditions(void);
static void print_status(void);
-/* load all game graphics in required sizes,
- return 0 on failure */
-int PrerenderGraphics()
-{
- SDL_Rect rt;
- int i;
-
- for(i = 0; i < POSITIONED_IMAGES; i++)
- {
- SetRect(&rt, img_pos[i]);
- if(!SetImage(img_ids[i], &rt))
- return 0;
- }
-
- rt.w = 10000; /* these images may be long */
- rt.h = CONSOLE_SCREEN_H * GetImage(IMG_CONSOLE_LED)->h;
- if(!SetImage(IMG_LEDNUMS, &rt))
- return 0;
- if(!SetImage(IMG_LED_NEG_SIGN, &rt))
- return 0;
-
- return 1;
-}
-
/* --- MAIN GAME FUNCTION!!! --- */
int game(void)
{
- Uint32 last_time, now_time;
+ Uint32 timer = 0;
int num_frames;
DEBUGMSG(debug_game, "Entering game():\n");
//see if the option matches the actual screen
if (Opts_GetGlobalOpt(FULLSCREEN) == !(GetScreen()->flags & SDL_FULLSCREEN) )
- {
- ;//SwitchScreenMode();
- }
+ {
+ ;//SwitchScreenMode(); //Huh??
+ }
/* most code moved into smaller functions (game_*()): */
if (!game_initialize())
{
fprintf(stderr, "\ngame_initialize() failed!");
- /* return 0 so we go back to Options screen - maybe */
- /* player simply has all operations deselected */
-// free_on_exit();
return 0;
}
+
if (Opts_HelpMode()) {
game_handle_help();
game_cleanup();
return GAME_OVER_OTHER;
}
+
-
/* --- MAIN GAME LOOP: --- */
do
{
/* reset or increment various things with each loop: */
frame++;
- last_time = SDL_GetTicks();
old_tux_img = tux_img;
tux_pressing = 0;
@@ -280,6 +290,13 @@
laser.alive--;
}
+ /* Check for server messages if we are playing a LAN game: */
+#ifdef HAVE_LIBSDL_NET
+ if(Opts_LanMode())
+ {
+ game_handle_net_messages();
+ }
+#endif
/* Most code now in smaller functions: */
// 1. Check for user input
@@ -297,9 +314,8 @@
// 3. Redraw:
game_draw();
// 4. Figure out if we should leave loop:
- game_status = check_exit_conditions();
+ game_status = check_exit_conditions();
-
/* If we're in "PAUSE" mode, pause! */
if (paused)
{
@@ -319,22 +335,15 @@
}
#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);
- }
+ Throttle(MS_PER_FRAME, &timer);
+
}
while(GAME_IN_PROGRESS == game_status);
/* END OF MAIN GAME LOOP! */
+
DEBUGCODE(debug_game) print_exit_conditions();
/* TODO: need better "victory" screen with animation, special music, etc., */
@@ -361,7 +370,6 @@
do
{
frame++;
- last_time = SDL_GetTicks();
while (SDL_PollEvent(&event) > 0)
{
@@ -376,8 +384,6 @@
if (current_bkgd() )
SDL_BlitSurface(current_bkgd(), NULL, GetScreen(), NULL);
-
-
/* draw flashing victory message: */
if (((frame / 2) % 4))
{
@@ -417,20 +423,14 @@
/* draw_console_image(tux_img);*/
SDL_Flip(GetScreen());
-
- now_time = SDL_GetTicks();
-
- if (now_time < last_time + MS_PER_FRAME)
- SDL_Delay(last_time + MS_PER_FRAME - now_time);
+ Throttle(MS_PER_FRAME, &timer);
}
while (looping);
break;
}
case GAME_OVER_ERROR:
- {
- printf("\ngame() exiting with error");
- }
+ DEBUGMSG(debug_game, "game() exiting with error:\n");
case GAME_OVER_LOST:
case GAME_OVER_OTHER:
{
@@ -445,7 +445,6 @@
do
{
frame++;
- last_time = SDL_GetTicks();
while (SDL_PollEvent(&event) > 0)
{
@@ -460,10 +459,7 @@
SDL_BlitSurface(GetImage(IMG_GAMEOVER), NULL, GetScreen(), &dest_message);
SDL_Flip(GetScreen());
- now_time = SDL_GetTicks();
-
- if (now_time < last_time + MS_PER_FRAME)
- SDL_Delay(last_time + MS_PER_FRAME - now_time);
+ Throttle(MS_PER_FRAME, &timer);
}
while (looping);
@@ -498,15 +494,343 @@
{
/* program exits: */
cleanup();
+ DEBUGMSG(debug_game, "Leaving game() from window close\n");
return 1;
}
else
{
/* return to title() screen: */
+ DEBUGMSG(debug_game, "Leaving game() normally\n");
return game_status;
}
}
+
+
+
+
+
+#ifdef HAVE_LIBSDL_NET
+/***************** Functions for LAN support *****************/
+
+/*Examines the network messages from the buffer and calls
+ appropriate function accordingly*/
+
+void game_handle_net_messages(void)
+{
+ char buf[NET_BUF_LEN];
+ int done = 0;
+ while(!done)
+ {
+ switch(LAN_NextMsg(buf))
+ {
+ case 1: //Message received (e.g. a new question):
+ game_handle_net_msg(buf);
+ break;
+ case 0: //No more messages:
+ done = 1;
+ break;
+ case -1: //Error in networking or server:
+ game_cleanup();
+ game_status = GAME_OVER_ERROR;
+ default:
+ {}
+ }
+ }
+}
+
+
+void game_handle_net_msg(char* buf)
+{
+ DEBUGMSG(debug_lan, "Received server message: %s\n", buf);
+
+ if(strncmp(buf, "PLAYER_MSG", strlen("PLAYER_MSG")) == 0)
+ {
+ printf("buf is %s\n", buf);
+ }
+
+ 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
+ DEBUGCODE(debug_game) 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
+ DEBUGCODE(debug_game) print_current_quests();
+ }
+
+ else if(strncmp(buf, "TOTAL_QUESTIONS", strlen("TOTAL_QUESTIONS")) == 0)
+ {
+ sscanf(buf,"%*s %d", &total_questions_left);
+ if(!total_questions_left)
+ game_over_other = 1;
+ }
+
+ else if(strncmp(buf, "CONNECTED_PLAYERS", strlen("CONNECTED_PLAYERS")) == 0)
+ {
+ connected_players_recvd(buf);
+ }
+
+ else if(strncmp(buf, "UPDATE_SCORE", strlen("UPDATE_SCORE")) == 0)
+ {
+ update_score_recvd(buf);
+ }
+
+ else if(strncmp(buf, "MISSION_ACCOMPLISHED", strlen("MISSION_ACCOMPLISHED")) == 0)
+ {
+ game_over_won = 1;
+ }
+ else
+ {
+ DEBUGMSG(debug_game, "Unrecognized message from server: %s\n", buf);
+ }
+}
+
+
+int add_quest_recvd(char* buf)
+{
+ /* Empty slots indicated by question_id == -1 */
+ MC_FlashCard* fc = search_queue_by_id(-1);
+
+ DEBUGMSG(debug_game, "Enter add_quest_recvd(), buf is: %s\n", buf);
+
+ // if fc = NULL means no empty slot for question
+ if(!buf)
+ {
+ printf("NULL buf\n");
+ return 0;
+ }
+
+ if(!fc)
+ {
+ printf("NULL fc - no empty slot for question\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;
+ }
+
+ DEBUGCODE(debug_game) print_current_quests();
+
+ /* If we have an open comet slot, put question in: */
+
+ if(num_attackers > 0)
+ if(add_comet())
+ num_attackers--;
+
+ return 1;
+}
+
+
+int remove_quest_recvd(char* buf)
+{
+ int id = 0;
+ char* p = NULL;
+ MC_FlashCard* fc = NULL;
+ comet_type* comet_screen;
+
+ if(!buf)
+ return 0;
+
+ p = strchr(buf, '\t');
+ if(!p)
+ return 0;
+
+ p++;
+ id = atoi(p);
+
+ DEBUGMSG(debug_game, "remove_quest_recvd() for id = %d\n", id);
+
+ if(id < 1) // The question_id can never be negative or zero
+ return 0;
+
+ comet_screen = search_comets_by_id(id);
+ fc = search_queue_by_id(id);
+ if(!comet_screen && !fc)
+ return 0;
+
+ if(comet_screen)
+ {
+ DEBUGMSG(debug_game, "comet on screen found with question_id = %d\n", id);
+ erase_comet_on_screen(comet_screen);
+ }
+
+ //NOTE: normally the question should no longer be in the queue,
+ //so the next statement should not get executed:
+ if(fc)
+ {
+ DEBUGMSG(debug_game,
+ "Note - request to erase question still in queue: %s\n",
+ fc->formula_string);
+ MC_ResetFlashCard(fc);
+ }
+
+ return 1;
+}
+
+
+/* Here we have been told how many LAN players are still */
+/* in the game. This should always be followed by a series */
+/* of UPDATE_SCORE messages, each with the name and score */
+/* of a player. We clear out the array to get rid of anyone */
+/* who has disconnected. */
+int connected_players_recvd(char* buf)
+{
+ int n = 0;
+ int i = 0;
+ char* p = NULL;
+
+ if(!buf)
+ return 0;
+
+ p = strchr(buf, '\t');
+ if(!p)
+ return 0;
+ p++;
+ n = atoi(p);
+
+ DEBUGMSG(debug_game, "connected_players_recvd() for n = %d\n", n);
+
+ if(n < 0 || n > MAX_CLIENTS)
+ {
+ fprintf(stderr, "connected_players_recvd() - illegal value: %d\n", n);
+ return -1;
+ }
+ lan_players = n;
+
+ /* Reset array - we should be getting new values in immediately */
+ /* following messages. */
+ for(i = 0; i < MAX_CLIENTS; i++)
+ {
+ lan_pnames[i][0] = '\0';
+ lan_pscores[i] = -1;
+ }
+ return n;
+}
+
+/* Receive the name and current score of a currently-connected */
+/* LAN player. */
+int update_score_recvd(char* buf)
+{
+ int i = 0;
+ char* p = NULL;
+
+ if(buf == NULL)
+ return 0;
+ // get i:
+ p = strchr(buf, '\t');
+ if(!p)
+ return 0;
+ p++;
+ i = atoi(p);
+
+ //get name:
+ p = strchr(p, '\t');
+ if(!p)
+ return 0;
+ p++;
+ strncpy(lan_pnames[i], p, NAME_SIZE);
+ //This has most likely copied the score field as well, so replace the
+ //tab delimiter with a null to terminate the string:
+ {
+ char* p2 = strchr(lan_pnames[i], '\t');
+ if (p2)
+ *p2 = '\0';
+ }
+
+ //Now get score:
+ p = strchr(p, '\t');
+ if(p)
+ lan_pscores[i] = atoi(p);
+
+ DEBUGMSG(debug_lan, "update_score_recvd() - buf is: %s\n", buf);
+ DEBUGMSG(debug_lan, "i is: %d\tname is: %s\tscore is: %d\n",
+ i, lan_pnames[i], lan_pscores[i]);
+
+ return 1;
+}
+
+/* Return a pointer to an empty comet slot, */
+/* returning NULL if no vacancy found: */
+
+MC_FlashCard* search_queue_by_id(int id)
+{
+ int i = 0;
+ for(i = 0; i < QUEST_QUEUE_SIZE; i++)
+ {
+ if(quest_queue[i].question_id == id)
+ return &quest_queue[i];
+ }
+ //if we don't find a match:
+ return NULL;
+}
+
+
+comet_type* search_comets_by_id(int id)
+{
+ int i;
+ for (i = 0; i < Opts_MaxComets(); i++)
+ {
+ if (comets[i].flashcard.question_id == id)
+ {printf("the question id is in slot %d\n",i);
+ return &comets[i];}
+ }
+
+ return NULL;
+}
+
+
+
+int erase_comet_on_screen(comet_type* comet)
+{
+ if(!comet)
+ return 0;
+ //setting expl to 0 starts comet explosion animation
+ comet->expl = 0;
+
+ //TODO consider more elaborate sound or animation
+ playsound(SND_SIZZLE);
+
+ return 1;
+}
+
+#endif
+
+/* Print 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 < Opts_MaxComets(); i++)
+ {
+ if(comets[i].alive == 1)
+ printf("Comet %d - question %d:\t%s\n", i, comets[i].flashcard.question_id, comets[i].flashcard.formula_string);
+
+ }
+ printf("--------------Question Queue-----------------\n");
+ for(i = 0; i < QUEST_QUEUE_SIZE; i++)
+ {
+ if(quest_queue[i].question_id != -1)
+ printf("quest_queue %d - question %d:\t%s\n", i, quest_queue[i].question_id, quest_queue[i].formula_string);
+ else
+ printf("quest_queue %d:\tEmpty\n", i);
+ }
+ printf("------------------------------------------\n");
+}
+
+
+
+
/*
Set one to four lines of text to display at the game's start. Eventually
this should stylishly fade out over the first few moments of the game.
@@ -521,12 +845,14 @@
start_message_chosen = 1;
}
+
+
int game_initialize(void)
{
int i,img;
DEBUGMSG(debug_game,"Entering game_initialize()\n");
-
+
/* Clear window: */
SDL_FillRect(GetScreen(), NULL, SDL_MapRGB(GetScreen()->format, 0, 0, 0));
SDL_Flip(GetScreen());
@@ -535,57 +861,75 @@
gameover_counter = -1;
user_quit_received = 0;
- /* Start MathCards backend: */
+ /* Make sure we don't try to call network code if we built without */
+ /* network support: */
+ /* NOTE with this check it should be safe to assume we have SDL_net */
+ /* for the rest of this file if Opts_LanMode() == 1 */
+#ifndef HAVE_LIBSDL_NET
+ Opts_SetLanMode(0);
+#endif
+
+ /* Start MathCards backend, unless we are getting questions from network: */
/* FIXME may need to move this into tuxmath.c to accomodate option */
/* to use MC_StartUsingWrongs() */
/* NOTE MC_StartGame() will return 0 if the list length is zero due */
/* (for example) to all math operations being deselected */
- if (!MC_StartGame())
+ /* NOTE if we are playing a network game, MC_StartGame() has already */
+ /* been called on the server by the time we get to here: */
+
+ if(!Opts_LanMode())
{
- fprintf(stderr, "\nMC_StartGame() failed!");
- return 0;
+ if (!MC_StartGame())
+ {
+ fprintf(stderr, "\nMC_StartGame() failed!");
+ return 0;
+ }
+ DEBUGMSG(debug_mathcards | debug_game,"MC_StartGame() finished.\n")
}
- DEBUGMSG(debug_mathcards | debug_game,"MC_StartGame() finished.\n")
+ else
+ {
+ /* Reset question queue and player name/score lists: */
+ int i;
+ for(i = 0; i < QUEST_QUEUE_SIZE; i ++)
+ MC_ResetFlashCard(&(quest_queue[i]));
+
+ for(i = 0; i < MAX_CLIENTS; i++)
+ {
+ lan_pnames[i][0] = '\0';
+ lan_pscores[i] = -1;
+ }
+ }
+
/* Allocate memory */
comets = NULL; // set in case allocation fails partway through
cities = NULL;
penguins = NULL;
steam = NULL;
comets = (comet_type *) malloc(MAX_MAX_COMETS * sizeof(comet_type));
- if (comets == NULL) {
+ if (comets == NULL)
+ {
printf("Allocation of comets failed");
return 0;
}
- else {
- for (i = 0; i < MAX_MAX_COMETS; ++i)
- {
- comets[i].flashcard = MC_AllocateFlashcard();
- if (!MC_FlashCardGood(&comets[i].flashcard) )
- {
- //something's wrong
- printf("Allocation of flashcard %d failed\n", i);
- for (; i >= 0; --i) //free anything we've already gotten
- MC_FreeFlashcard(&comets[i].flashcard);
- return 0;
- }
- }
- }
- DEBUGMSG(debug_game,"Flashcards allocated.\n");
-
cities = (city_type *) malloc(NUM_CITIES * sizeof(city_type));
- if (cities == NULL) {
+ if (cities == NULL)
+ {
printf("Allocation of cities failed");
return 0;
}
+
penguins = (penguin_type *) malloc(NUM_CITIES * sizeof(penguin_type));
- if (penguins == NULL) {
+ if (penguins == NULL)
+ {
printf("Allocation of penguins failed");
return 0;
}
+
steam = (steam_type *) malloc(NUM_CITIES * sizeof(steam_type));
- if (steam == NULL) {
+ if (steam == NULL)
+ {
printf("Allocation of steam failed");
return 0;
}
@@ -612,6 +956,7 @@
slowdown = 0;
score = 0;
demo_countdown = 2000;
+ total_questions_left = 0;
level_start_wait = LEVEL_START_WAIT_START;
neg_answer_picked = 0;
@@ -645,12 +990,12 @@
}
num_cities_alive = NUM_CITIES;
- num_comets_alive = 0;
igloo_vertical_offset = GetImage(IMG_CITY_BLUE)->h - GetImage(IMG_IGLOO_INTACT)->h;
/* Create and position the penguins and steam */
- for (i = 0; i < NUM_CITIES; i++) {
+ for (i = 0; i < NUM_CITIES; i++)
+ {
penguins[i].status = PENGUIN_HAPPY;
penguins[i].counter = 0;
penguins[i].x = cities[i].x;
@@ -660,10 +1005,12 @@
steam[i].counter = 0;
}
- if (Opts_BonusCometInterval()) {
+ if (Opts_BonusCometInterval())
+ {
bonus_comet_counter = Opts_BonusCometInterval() + 1;
- DEBUGMSG(debug_game,"\nInitializing with bonus_comet_counter = %d\n",bonus_comet_counter)
+ DEBUGMSG(debug_game,"\nInitializing with bonus_comet_counter = %d\n",bonus_comet_counter);
}
+
extra_life_earned = 0;
cloud.status = EXTRA_LIFE_OFF;
@@ -707,6 +1054,10 @@
void game_cleanup(void)
{
+#ifdef HAVE_LIBSDL_NET
+ LAN_Cleanup();
+#endif
+
/* Free background: */
if (bkgd != NULL)
{
@@ -736,9 +1087,10 @@
}
#endif
- DEBUGMSG(debug_game, "Leaving game():\n");
+ DEBUGMSG(debug_game, "Leaving game_cleanup():\n");
}
+
void game_handle_help(void)
{
const int left_edge = 140;
@@ -855,39 +1207,51 @@
help_add_comet("56 / 8 = ?", "7");
comets[0].y = 2*(GetScreen()->h)/3; // start it low down
+
while (comets[0].alive && !(quit_help = help_renderframe_exit()));
+
if (quit_help)
return;
frame_start = frame;
+
while ((frame-frame_start < 3*FPS) && !(quit_help = help_renderframe_exit()));
+
if (quit_help)
return;
help_controls.laser_enabled = 1;
- game_set_message(&s1,_("You can fix the igloos"),left_edge,100);
- game_set_message(&s2,_("by stopping bonus comets."),left_edge,135);
+ game_set_message(&s1,_("You can fix the igloos"), left_edge,100);
+ game_set_message(&s2,_("by stopping bonus comets."), left_edge,135);
help_add_comet("2 + 2 = ?", "4");
comets[0].bonus = 1;
frame_start = frame;
+
while (comets[0].alive && (frame-frame_start < 50) && !(quit_help = help_renderframe_exit()));
+
if (quit_help)
return;
if (comets[0].alive)
speed = 0;
game_set_message(&s3,_("Zap it now!"),left_edge,225);
+
while (comets[0].alive && !(quit_help = help_renderframe_exit()));
+
if (quit_help)
return;
game_set_message(&s1,_("Great job!"),left_edge,100);
game_clear_message(&s2);
game_clear_message(&s3);
frame_start = frame;
+
while ((frame-frame_start < 2*FPS) && !(quit_help = help_renderframe_exit()));
+
if (quit_help)
return;
check_extra_life();
frame_start = frame;
+
while ((frame-frame_start < 10*FPS) && !(quit_help = help_renderframe_exit()));
+
if (quit_help)
return;
@@ -898,6 +1262,7 @@
game_set_message(&s4,_("Do it now, and then play!"),left_edge,225);
help_controls.x_is_blinking = 1;
+
while (!help_renderframe_exit());
}
@@ -947,7 +1312,7 @@
comets[0].alive = 1;
comets[0].expl = -1;
comets[0].answer = atoi(ans_str);
- num_comets_alive = 1;
+// num_comets_alive = 1;
comets[0].city = 0;
comets[0].x = cities[0].x;
comets[0].y = 0;
@@ -985,10 +1350,11 @@
void game_write_message(const game_message *msg)
{
- SDL_Surface *surf;
+ SDL_Surface* surf;
SDL_Rect rect;
- if (strlen(msg->message) > 0) {
+ if (strlen(msg->message) > 0)
+ {
surf = BlackOutline( _(msg->message), DEFAULT_HELP_FONT_SIZE, &white);
rect.w = surf->w;
rect.h = surf->h;
@@ -1047,87 +1413,89 @@
/* Demo mode! */
{
- static int demo_answer = 0;
- static int answer_digit = 0;
- static int picked_comet=-1;
+ static int demo_answer = 0;
+ static int answer_digit = 0;
+ static int picked_comet=-1;
- if (picked_comet == -1 && (rand() % 10) < 3)
- {
- /* Demo mode! Randomly pick a comet to destroy: */
- picked_comet = (rand() % MAX_COMETS);
-
- if (!(comets[picked_comet].alive &&
- comets[picked_comet].expl == -1)
- || comets[picked_comet].y < 80)
+ if (picked_comet == -1 && (rand() % 10) < 3)
{
- picked_comet = -1;
- }
- else
- {
- /* found a comet to blow up! */
- demo_answer = comets[picked_comet].answer;
- if ((rand() % 3) < 1)
- demo_answer--; // sometimes get it wrong on purpose
+ /* Demo mode! Randomly pick a comet to destroy: */
+ picked_comet = (rand() % Opts_MaxComets());
- DEBUGMSG(debug_game, "Demo mode, comet %d attacked with answer %d\n",picked_comet,demo_answer);
- /* handle negative answer: */
- if (demo_answer < 0)
+ if (!(comets[picked_comet].alive &&
+ comets[picked_comet].expl == -1)
+ || comets[picked_comet].y < 80)
{
- demo_answer = -demo_answer;
- neg_answer_picked = 1;
+ picked_comet = -1;
}
- if (demo_answer >= 100)
- answer_digit = 0;
- else if (demo_answer >= 10)
- answer_digit = 1;
else
- answer_digit = 2;
+ {
+ /* found a comet to blow up! */
+ demo_answer = comets[picked_comet].answer;
+ if ((rand() % 3) < 1)
+ demo_answer--; // sometimes get it wrong on purpose
+
+ DEBUGMSG(debug_game, "Demo mode, comet %d attacked with answer %d\n", picked_comet,demo_answer);
+
+ /* handle negative answer: */
+ if (demo_answer < 0)
+ {
+ demo_answer = -demo_answer;
+ neg_answer_picked = 1;
+ }
+ if (demo_answer >= 100)
+ answer_digit = 0;
+ else if (demo_answer >= 10)
+ answer_digit = 1;
+ else
+ answer_digit = 2;
+ }
}
- }
- /* Add a digit: */
- if (picked_comet != -1 && (frame % 5) == 0 && (rand() % 10) < 8)
- {
- tux_pressing = 1;
-
- if (answer_digit < 3)
+ /* Add a digit: */
+ if (picked_comet != -1 && (frame % 5) == 0 && (rand() % 10) < 8)
{
- digits[0] = digits[1];
- digits[1] = digits[2];
+ tux_pressing = 1;
- if (answer_digit == 0)
+ if (answer_digit < 3)
{
- digits[2] = demo_answer / 100;
+ digits[0] = digits[1];
+ digits[1] = digits[2];
+
+ if (answer_digit == 0)
+ {
+ digits[2] = demo_answer / 100;
+ }
+ else if (answer_digit == 1)
+ {
+ digits[2] = (demo_answer % 100) / 10;
+ }
+ else if (answer_digit == 2)
+ {
+ digits[2] = (demo_answer % 10);
+ }
+
+ answer_digit++;
}
- else if (answer_digit == 1)
+ else
{
- digits[2] = (demo_answer % 100) / 10;
+ /* "Press Return" */
+ DEBUGMSG(debug_game, "Demo mode firing with these digits: %d%d%d\n",
+ digits[0], digits[1], digits[2]);
+ doing_answer = 1;
+ picked_comet = -1;
}
- else if (answer_digit == 2)
- {
- digits[2] = (demo_answer % 10);
- }
+ }
- answer_digit++;
- }
- else
- {
- /* "Press Return" */
- DEBUGMSG(debug_game, "Demo mode firing with these digits: %d%d%d\n",digits[0],digits[1],digits[2]);
- doing_answer = 1;
- picked_comet = -1;
- }
+ /* Count down counter: */
+ demo_countdown--;
}
-
- /* Count down counter: */
- demo_countdown--;
}
-}
void game_handle_answer(void)
{
int i, j, lowest, lowest_y;
- char ans[MC_MAX_DIGITS+2]; //extra space for negative, and for final '\0'
+ char ans[MC_MAX_DIGITS + 2]; //extra space for negative, and for final '\0'
Uint32 ctime;
if (!doing_answer)
@@ -1136,11 +1504,7 @@
}
doing_answer = 0;
-/*
- num = (digits[0] * 100 +
- digits[1] * 10 +
- digits[2]);
-*/
+
/* negative answer support DSB */
ans[0] = '-'; //for math questions only, this is just replaced.
@@ -1149,36 +1513,15 @@
ans[j] = digits[i] + '0';
ans[j] = '\0';
-/*
- if (neg_answer_picked)
- {
- ans[0] = '-';
- for (i = j = 0; i < MC_MAX_DIGITS; ++i)
- {
- if (digits[i] == 0)
- continue;
- ans[++j] = digits[i] + '0';
- }
- }
- else
- {
- for (i = j = 0; i < MC_MAX_DIGITS; ++i)
- {
- if (digits[i] == 0)
- continue;
- ans[j++] = digits[i] + '0';
- }
- }
-*/
+
/* Pick the lowest comet which has the right answer: */
/* FIXME: do we want it to prefer bonus comets to regular comets? */
lowest_y = 0;
lowest = -1;
- for (i = 0; i < MAX_COMETS; i++)
+ for (i = 0; i < Opts_MaxComets(); i++)
{
- mcdprintf("Comparing '%s' with '%s'\n", comets[i].flashcard.answer_string, ans);
if (comets[i].alive &&
comets[i].expl == -1 &&
//comets[i].answer == num &&
@@ -1190,17 +1533,22 @@
}
}
- /* If there was an comet with this answer, destroy it! */
+ /* If there was a comet with this answer, destroy it! */
if (lowest != -1) /* -1 means no comet had this answer */
{
- MC_AnsweredCorrectly(&(comets[lowest].flashcard));
-
+ float t;
/* Store the time the question was present on GetScreen() (do this */
/* in a way that avoids storing it if the time wrapped around */
ctime = SDL_GetTicks();
- if (ctime > comets[lowest].time_started) {
- MC_AddTimeToList((float)(ctime - comets[lowest].time_started)/1000);
- }
+ if (ctime > comets[lowest].time_started)
+ t = ((float)(ctime - comets[lowest].time_started)/1000);
+ else
+ t = -1; //Mathcards will ignore t == -1
+ /* Tell Mathcards or the server that we answered correctly: */
+ if(Opts_LanMode())
+ LAN_AnsweredCorrectly(comets[lowest].flashcard.question_id, t);
+ else
+ MC_AnsweredCorrectly(comets[lowest].flashcard.question_id, t);
/* Destroy comet: */
@@ -1226,8 +1574,7 @@
#endif
}
-
- /* FIXME maybe should move this into game_handle_tux() */
+ /* Pick Tux animation: */
/* 50% of the time.. */
if ((rand() % 10) < 5)
{
@@ -1284,6 +1631,7 @@
s2.alpha -= SDL_ALPHA_OPAQUE / LEVEL_START_WAIT_START;
s3.alpha -= SDL_ALPHA_OPAQUE / LEVEL_START_WAIT_START;
s4.alpha -= SDL_ALPHA_OPAQUE / LEVEL_START_WAIT_START;
+ DEBUGMSG(debug_game, "alpha = %d\n", s1.alpha);
level_start_wait--;
if (level_start_wait > LEVEL_START_WAIT_START / 4)
@@ -1307,14 +1655,9 @@
/* If Tux pressed a button, pick a new (different!) stance: */
if (tux_pressing)
{
- num_frames = GetSprite(ANIM_TUX_CONSOLE)->num_frames;
- do
- {
- tux_img = GetSprite(ANIM_TUX_CONSOLE)->frame[rand() % num_frames];
- }
+ do { tux_img = IMG_TUX_CONSOLE1 + (rand() % 4); }
while (tux_img == old_tux_img);
-
- PlaySound(sounds[SND_CLICK]);
+ playsound(SND_TOCK);
}
/* If Tux is being animated, show the animation: */
@@ -1349,22 +1692,23 @@
int i, this_city;
Uint32 ctime;
- num_comets_alive = 0;
+// num_comets_alive = 0;
/* Clear the threatened flag on each city */
for (i = 0; i < NUM_CITIES; i++)
cities[i].threatened = 0;
- for (i = 0; i < MAX_COMETS; i++)
+ for (i = 0; i < Opts_MaxComets(); i++)
{
if (comets[i].alive)
{
- num_comets_alive++;
+// num_comets_alive++;
this_city = comets[i].city;
/* Update comet position */
comets[i].x = comets[i].x + 0; /* no lateral motion for now! */
/* Make bonus comet move faster at chosen ratio: */
+ /* NOTE y increment scaled to make game play similar at any resolution */
if (comets[i].bonus)
{
comets[i].y += speed * Opts_BonusSpeedRatio() *
@@ -1385,7 +1729,10 @@
comets[i].expl == -1)
{
/* Tell MathCards about it - question not answered correctly: */
- MC_NotAnsweredCorrectly(&(comets[i].flashcard));
+ if(Opts_LanMode())
+ LAN_NotAnsweredCorrectly(comets[i].flashcard.question_id);
+ else
+ MC_NotAnsweredCorrectly(comets[i].flashcard.question_id);
/* Store the time the question was present on screen (do this */
/* in a way that avoids storing it if the time wrapped around */
@@ -1470,14 +1817,18 @@
}
}
+ /* FIXME for the LAN game, the adding of comets needs to take place in */
+ /* check_messages() when new questions come in from the server. For */
+ /* ease of understanding, we should do it at the same place in the game */
+ /* loop for the non-LAN (i.e. local MC_*() functions) game - DSB */
/* add more comets if needed: */
- if (!Opts_HelpMode() && level_start_wait == 0 &&
- (frame % 20) == 0) /* FIXME:do we want this to vary with comet speed?*/
+ if (!Opts_HelpMode() && level_start_wait == 0) //&&
+ // (frame % 20) == 0)
{
/* num_attackers is how many comets are left in wave */
if (num_attackers > 0)
{
- if ((rand() % 2) == 0 || num_comets_alive == 0)
+// if ((rand() % 2) == 0 || num_comets_alive() == 0) NOTE also caused timing issue
{
if (add_comet())
{
@@ -1487,9 +1838,10 @@
}
else
{
- if (num_comets_alive == 0)
+ if (num_comets_alive() == 0)
{
- if (!check_extra_life()) {
+ if (!check_extra_life())
+ {
/* Time for the next wave! */
wave++;
reset_level();
@@ -1729,6 +2081,7 @@
return 1;
DEBUGCODE(debug_game)
print_status();
+
if (extra_life_earned) {
/* Check to see if any ingloo has been hit */
fewest_hits_left = 2;
@@ -1747,7 +2100,7 @@
cloud.y = GetScreen()->h/3;
cloud.city = fewest_index;
bonus_comet_counter = Opts_BonusCometInterval()+1;
-
+
DEBUGMSG(debug_game, "Bonus comet counter restored to %d\n",bonus_comet_counter);
if (cloud.city < NUM_CITIES/2)
@@ -1765,14 +2118,16 @@
DEBUGCODE(debug_game)
print_status();
return 1;
- } else
+ }
+ else
return 0;
}
+
void game_handle_extra_life(void)
{
// This handles the animation sequence during the rebuilding of an igloo
- int i,igloo_top,num_below_igloo,direction;
+ int i, igloo_top, num_below_igloo, direction;
if (cloud.status == EXTRA_LIFE_ON) {
@@ -1891,7 +2246,7 @@
fgcolor = SDL_MapRGB(GetScreen()->format, 64, 96, 64);
if (old_wave != wave)
{
- DEBUGMSG(debug_game,"Wave %d\n", wave)
+ DEBUGMSG(debug_game,"Wave %d\n", wave);
old_wave = wave;
bgcolor = SDL_MapRGB(GetScreen()->format,
64,
@@ -1930,13 +2285,14 @@
/* draw last (i.e. in front), as they can overlap */
void game_draw_comets(void)
{
+
int i;
SDL_Surface* img = NULL;
SDL_Rect dest;
char* comet_str;
/* First draw regular comets: */
- for (i = 0; i < MAX_COMETS; i++)
+ for (i = 0; i < Opts_MaxComets(); i++)
{
if (comets[i].alive && !comets[i].bonus)
{
@@ -1977,7 +2333,7 @@
}
/* Now draw any bonus comets: */
- for (i = 0; i < MAX_COMETS; i++)
+ for (i = 0; i < Opts_MaxComets(); i++)
{
if (comets[i].alive && comets[i].bonus)
{
@@ -2002,14 +2358,11 @@
comet_str = comets[i].flashcard.answer_string;
}
- /* Move image index to bonus range: */
-
/* Draw it! */
dest.x = comets[i].x - (img->w / 2);
dest.y = comets[i].y - img->h;
dest.w = img->w;
dest.h = img->h;
-
SDL_BlitSurface(img, NULL, GetScreen(), &dest);
if (comet_str != NULL)
{
@@ -2019,6 +2372,8 @@
}
}
+
+
void game_draw_cities(void)
{
int i, j, current_layer, max_layer;
@@ -2208,7 +2563,8 @@
sprintf(str, "%d", wave);
draw_numbers(str, offset+GetImage(IMG_WAVE)->w + (GetImage(IMG_NUMBERS)->w / 10), 0);
- if (Opts_KeepScore() )
+ /* In LAN mode, we show the server-generated score: */
+ if (Opts_KeepScore() && !Opts_LanMode())
{
/* Draw "score" label: */
dest.x = (GetScreen()->w - ((GetImage(IMG_NUMBERS)->w / 10) * 7) -
@@ -2226,9 +2582,10 @@
0);
}
- /* Draw other players' scores */
+ /* Draw other players' scores (turn-based single machine multiplayer) */
if (mp_get_parameter(PLAYERS) && mp_get_parameter(MODE) == SCORE_SWEEP )
{
+ int i;
for (i = 0; i < mp_get_parameter(PLAYERS); ++i)
{
SDL_Surface* score;
@@ -2246,6 +2603,31 @@
}
}
+ /* Draw other players' scores (LAN game) */
+ if (Opts_LanMode())
+ {
+ int entries = 0;
+ for (i = 0; i < MAX_CLIENTS; i++)
+ {
+ if(lan_pscores[i] >= 0)
+ {
+ SDL_Surface* score;
+ snprintf(str, 64, "%s: %d", lan_pnames[i], lan_pscores[i]);
+ score = BlackOutline(str, DEFAULT_MENU_FONT_SIZE, &white);
+ if(score)
+ {
+ SDL_Rect loc;
+ loc.w = score->w;
+ loc.h = score->h;
+ loc.x = 0;
+ loc.y = score->h * (entries + 2);
+ SDL_BlitSurface(score, NULL, screen, &loc);
+ entries++;
+ }
+ }
+ }
+ }
+
/* Draw stop button: */
if (!help_controls.x_is_blinking || (frame % 10 < 5)) {
dest.x = (GetScreen()->w - GetImage(IMG_STOP)->w);
@@ -2259,14 +2641,16 @@
int check_exit_conditions(void)
{
+// int x;
+
if (user_quit_received)
{
if (user_quit_received != GAME_OVER_WINDOW_CLOSE &&
user_quit_received != GAME_OVER_ESCAPE &&
user_quit_received != GAME_OVER_CHEATER)
{
- fprintf(stderr,"Unexpected value %d for user_quit_received\n", user_quit_received);
- return GAME_OVER_OTHER;
+ fprintf(stderr, "Unexpected value %d for user_quit_received\n", user_quit_received);
+ return GAME_OVER_OTHER;
}
return user_quit_received;
}
@@ -2282,30 +2666,53 @@
}
/* determine if game won (i.e. all questions in mission answered correctly): */
- if (MC_MissionAccomplished())
+ if(Opts_LanMode())
{
- DEBUGMSG(debug_game,"Mission accomplished!\n");
- return GAME_OVER_WON;
+ if(game_over_won)
+ return GAME_OVER_WON;
}
+ else
+ {
+ if (MC_MissionAccomplished())
+ {
+ DEBUGMSG(debug_game,"Mission accomplished!\n");
+ return GAME_OVER_WON;
+ }
+ }
+
/* Could have situation where mathcards doesn't have more questions */
/* even though not all questions answered correctly: */
- if (!MC_TotalQuestionsLeft())
+ if(Opts_LanMode())
{
- return GAME_OVER_OTHER;
+ if(game_over_other)
+ return GAME_OVER_OTHER;
}
+ else
+ {
+ if(!MC_TotalQuestionsLeft())
+ return GAME_OVER_OTHER;
+ }
+
+ //NOTE can't use this check in LAN mode because we don't know if the server has
+ //questions left
+
/* 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)
+ if (!Opts_LanMode())
{
- DEBUGMSG(debug_game, "ListQuestionsLeft() = %d ", MC_ListQuestionsLeft());
- DEBUGMSG(debug_game, "num_comets_alive = %d", num_comets_alive);
- return GAME_OVER_ERROR;
- }
+ if (!MC_ListQuestionsLeft() && !num_comets_alive())
+ {
+ fprintf(stderr, "Error - no questions left but game not over\n");
+ DEBUGMSG(debug_game, "ListQuestionsLeft() = %d ", MC_ListQuestionsLeft());
+ DEBUGMSG(debug_game, "num_comets_alive() = %d", num_comets_alive());
+ return GAME_OVER_ERROR;
+ }
+ }
- /* If using demo mode, see if counter has run out: */
+ /* If using demo mode, see if counter has run out: */
if (Opts_DemoMode())
{
if (demo_countdown <= 0 )
@@ -2316,6 +2723,7 @@
return GAME_IN_PROGRESS;
}
+
void print_exit_conditions(void)
{
printf("\ngame_status:\t");
@@ -2366,6 +2774,7 @@
}
}
+
/* Reset stuff for the next level! */
void reset_level(void)
{
@@ -2373,16 +2782,20 @@
int i;
int next_wave_comets;
int use_feedback;
- float comet_avg_height,height_differential;
+ float comet_avg_height, height_differential;
/* Clear all comets: */
- for (i = 0; i < MAX_COMETS; i++)
+ for (i = 0; i < Opts_MaxComets(); i++)
{
+ DEBUGCODE(debug_game)
+ {
+ if(comets[i].alive)
+ printf("Warning - resetting comets but comet[%d| still alive\n", i);
+ }
comets[i].alive = 0;
}
- num_comets_alive = 0;
/* Clear LED F: */
@@ -2390,8 +2803,6 @@
digits[i] = 0;
neg_answer_picked = 0;
-
-
/* Load random background image, but ensure it's different from this one: */
for (i = last_bkgd; i == last_bkgd; i = rand() % NUM_BKGDS);
@@ -2538,76 +2949,108 @@
int add_comet(void)
{
static int prev_city = -1;
- int i, found;
+ int i;
float y_spacing;
- /* Look for a free comet slot and see if all live comets are far */
- /* enough down to avoid overlap and keep formulas legible: */
- found = -1;
+ int com_found = -1;
+ int q_found = -1;
+
y_spacing = (GetImage(IMG_NUMS)->h) * 1.5;
- for (i = 0; i < MAX_COMETS && found == -1; i++)
+ /* Return if any previous comet too high up to create another one yet: */
+ for (i = 0; i < Opts_MaxComets(); i++)
{
if (comets[i].alive)
- {
if (comets[i].y < y_spacing)
{
- /* previous comet too high up to create another one yet: */
+ DEBUGMSG(debug_game,
+ "add_comet() - returning because comet[%d] not"
+ " far enough down: %f\n", i, comets[i].y);
return 0;
}
- }
- else /* non-living comet so we found a free slot: */
+ }
+
+ /* Now look for a free comet slot: */
+ for (i = 0; i < Opts_MaxComets(); i++)
+ {
+ if (!comets[i].alive)
{
- found = i;
+ com_found = i;
+ break;
}
}
-
- if (-1 == found)
+
+ if (-1 == com_found)
{
/* free comet slot not found - no comet added: */
+ DEBUGMSG(debug_game, "add_comet() called but no free comet slot\n");
+ DEBUGCODE(debug_game) print_current_quests();
return 0;
}
- /* Get math question for new comet - the following function fills in */
- /* the flashcard struct that is part of the comet struct: */
- if (!MC_NextQuestion(&(comets[found].flashcard)))
+ /* If playing in LAN mode, see if we have a question ready in */
+ /* our local queue: */
+
+ if(Opts_LanMode())
{
- /* no more questions available - cannot create comet. */
- return 0;
+ DEBUGCODE(debug_game) print_current_quests();
+ for (i = 0; i < QUEST_QUEUE_SIZE; i++)
+ {
+ if(quest_queue[i].question_id != -1)
+ {
+ DEBUGMSG(debug_game, "Found question_id %d, %s\n",
+ quest_queue[i].question_id,
+ quest_queue[i].formula_string);
+ q_found = i;
+ break;
+ }
+ }
+
+ if(q_found == -1)
+ {
+ DEBUGMSG(debug_game, "add_comet() called but no question available in queue\n");
+ return 0; DEBUGCODE(debug_game) print_current_quests();
+ }
}
- /* If we make it to here, create a new comet! */
+ /* Now we have a vacant comet slot at com_found and (if in LAN mode) */
+ /* a question for it at q_found. Now just copy: */
- comets[found].answer = comets[found].flashcard.answer;
-// /* The answer may be num1, num2, or num3, depending on format. */
-// switch (comets[found].flashcard.format)
-// {
-// case MC_FORMAT_ANS_LAST: /* e.g. num1 + num2 = ? */
-// {
-// comets[found].answer = comets[found].flashcard.num3;
-// break;
-// }
-// case MC_FORMAT_ANS_MIDDLE: /* e.g. num1 + ? = num3 */
-// {
-// comets[found].answer = comets[found].flashcard.num2;
-// break;
-// }
-// case MC_FORMAT_ANS_FIRST: /* e.g. ? + num2 = num3 */
-// {
-// comets[found].answer = comets[found].flashcard.num1;
-// break;
-// }
-// default: /* should not get to here if MathCards behaves correctly */
-// {
-// fprintf(stderr, "\nadd_comet() - invalid question format");
-// return 0;
-// }
-// }
+ if(Opts_LanMode())
+ {
+ MC_CopyCard(&(quest_queue[q_found]), &(comets[com_found].flashcard));
+ MC_ResetFlashCard(&(quest_queue[q_found]));
+ }
+ else // Not LAN mode - just get question with direct call:
+ {
+ if (!MC_NextQuestion(&(comets[com_found].flashcard)))
+ {
+ /* no more questions available - cannot create comet. */
+ return 0;
+ }
+ }
+
+ DEBUGCODE(debug_game)
+ {
+ printf("In add_comet(), card is\n");
+ print_card(comets[com_found].flashcard);
+ }
+ /* Make sure question is "sane" before we add it: */
+ if( (comets[com_found].flashcard.answer > 999)
+ ||(comets[com_found].flashcard.answer < -999))
+ {
+ printf("Warning, card with invalid answer encountered: %d\n",
+ comets[com_found].flashcard.answer);
+ MC_ResetFlashCard(&(comets[com_found].flashcard));
+ return 0;
+ }
- comets[found].alive = 1;
- num_comets_alive++;
+ /* If we make it to here, create a new comet!*/
+ comets[com_found].answer = comets[com_found].flashcard.answer;
+ comets[com_found].alive = 1;
+// num_comets_alive++;
/* Pick a city to attack that was not attacked last time */
/* (so formulas are less likely to overlap). */
@@ -2620,33 +3063,35 @@
prev_city = i;
/* Set in to attack that city: */
- comets[found].city = i;
+ comets[com_found].city = i;
/* Start at the top, above the city in question: */
- comets[found].x = cities[i].x;
- comets[found].y = 0;
- comets[found].zapped = 0;
+ comets[com_found].x = cities[i].x;
+ comets[com_found].y = 0;
+ comets[com_found].zapped = 0;
/* Should it be a bonus comet? */
- comets[found].bonus = 0;
+ comets[com_found].bonus = 0;
DEBUGMSG(debug_game, "bonus_comet_counter is %d\n",bonus_comet_counter);
- if (bonus_comet_counter == 1) {
+ if (bonus_comet_counter == 1)
+ {
bonus_comet_counter = 0;
- comets[found].bonus = 1;
+ comets[com_found].bonus = 1;
PlaySound(sounds[SND_BONUS_COMET]);
-
DEBUGMSG(debug_game, "Created bonus comet");
}
- DEBUGMSG(debug_game, "add_comet(): formula string is: %s", comets[found].flashcard.formula_string);
-
+ DEBUGMSG(debug_game, "add_comet(): formula string is: %s\n", comets[com_found].flashcard.formula_string);
+
/* Record the time at which this comet was created */
- comets[found].time_started = SDL_GetTicks();
-
+ comets[com_found].time_started = SDL_GetTicks();
+
/* comet slot found and question found so return successfully: */
return 1;
}
+
+
/* Draw numbers/symbols over the attacker: */
/* This draws the numbers related to the comets */
void draw_nums(const char* str, int x, int y)
@@ -2671,19 +3116,14 @@
/* the following code keeps the formula at least 8 pixels inside the window: */
if (cur_x < 8)
cur_x = 8;
- if (cur_x + (image_length) >=
- (GetScreen()->w - 8))
- cur_x = ((GetScreen()->w - 8) -
- (image_length));
+ if (cur_x + (image_length) >= (screen->w - 8))
+ cur_x = ((screen->w - 8) - (image_length));
-
/* Draw each character: */
-
for (i = 0; i < str_length; i++)
{
c = -1;
-
/* Determine which character to display: */
if (str[i] >= '0' && str[i] <= '9')
@@ -2743,45 +3183,38 @@
int i, cur_x, c;
SDL_Rect src, dest;
-
cur_x = x;
-
/* Draw each character: */
for (i = 0; i < strlen(str); i++)
- {
- c = -1;
+ {
+ c = -1;
+ /* Determine which character to display: */
+ if (str[i] >= '0' && str[i] <= '9')
+ c = str[i] - '0';
- /* Determine which character to display: */
+ /* Display this character! */
+ if (c != -1)
+ {
+ src.x = c * (images[IMG_NUMBERS]->w / 10);
+ src.y = 0;
+ src.w = (images[IMG_NUMBERS]->w / 10);
+ src.h = images[IMG_NUMBERS]->h;
- if (str[i] >= '0' && str[i] <= '9')
- c = str[i] - '0';
+ dest.x = cur_x;
+ dest.y = y;
+ dest.w = src.w;
+ dest.h = src.h;
+ SDL_BlitSurface(images[IMG_NUMBERS], &src,
+ screen, &dest);
- /* Display this character! */
-
- if (c != -1)
- {
- src.x = c * (GetImage(IMG_NUMBERS)->w / 10);
- src.y = 0;
- src.w = (GetImage(IMG_NUMBERS)->w / 10);
- src.h = GetImage(IMG_NUMBERS)->h;
-
- dest.x = cur_x;
- dest.y = y;
- dest.w = src.w;
- dest.h = src.h;
-
- SDL_BlitSurface(GetImage(IMG_NUMBERS), &src,
- GetScreen(), &dest);
-
-
- /* Move the 'cursor' one character width: */
- cur_x = cur_x + (GetImage(IMG_NUMBERS)->w / 10);
- }
+ /* Move the 'cursor' one character width: */
+ cur_x = cur_x + (images[IMG_NUMBERS]->w / 10);
}
+ }
}
@@ -2814,13 +3247,11 @@
SDL_BlitSurface(GetImage(IMG_PAUSED), NULL, GetScreen(), &dest);
SDL_UpdateRect(GetScreen(), 0, 0, 0, 0);
-
#ifndef NOSOUND
if (Opts_UsingSound())
Mix_PauseMusic();
#endif
-
do
{
while (SDL_PollEvent(&event))
@@ -2838,7 +3269,6 @@
}
while (!pause_done && !pause_quit);
-
#ifndef NOSOUND
if (Opts_UsingSound())
Mix_ResumeMusic();
@@ -2849,8 +3279,9 @@
+/* FIXME these ought to be in SDL_extras - DSB */
+
/* Draw a line: */
-
void draw_line(int x1, int y1, int x2, int y2, int red, int grn, int blu)
{
int dx, dy, tmp;
@@ -2926,34 +3357,34 @@
/* Assuming the X/Y values are within the bounds of this surface... */
if (x >= 0 && y >= 0 && x < surface->w && y < surface->h)
- {
+ {
/* Set the (correctly-sized) piece of data in the surface's RAM
to the pixel value sent in: */
- if (bpp == 1)
- *p = pixel;
- else if (bpp == 2)
- *(Uint16 *)p = pixel;
- else if (bpp == 3)
- {
- if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
- {
- p[0] = (pixel >> 16) & 0xff;
- p[1] = (pixel >> 8) & 0xff;
- p[2] = pixel & 0xff;
- }
- else
- {
- p[0] = pixel & 0xff;
- p[1] = (pixel >> 8) & 0xff;
- p[2] = (pixel >> 16) & 0xff;
- }
- }
- else if (bpp == 4)
- {
- *(Uint32 *)p = pixel;
- }
+ if (bpp == 1)
+ *p = pixel;
+ else if (bpp == 2)
+ *(Uint16 *)p = pixel;
+ else if (bpp == 3)
+ {
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
+ {
+ p[0] = (pixel >> 16) & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = pixel & 0xff;
+ }
+ else
+ {
+ p[0] = pixel & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = (pixel >> 16) & 0xff;
+ }
}
+ else if (bpp == 4)
+ {
+ *(Uint32 *)p = pixel;
+ }
+ }
#else
SDL_Rect dest;
@@ -3011,7 +3442,11 @@
SDL_BlitSurface(comet_img, NULL, GetScreen(), &dest);
/* draw number of remaining questions: */
- questions_left = MC_TotalQuestionsLeft();
+ if(Opts_LanMode())
+ questions_left = total_questions_left;
+ else
+ questions_left = MC_TotalQuestionsLeft();
+
sprintf(str, "%.4d", questions_left);
draw_numbers(str, nums_x, 0);
}
@@ -3400,15 +3835,17 @@
void add_score(int inc)
{
score += inc;
- DEBUGMSG(debug_game,"Score is now: %d\n", score)
+ DEBUGMSG(debug_game,"Score is now: %d\n", score);
}
void reset_comets(void)
{
- int i =0;
- for (i = 0; i < MAX_COMETS; i++)
+ int i = 0;
+ comet_counter = 0;
+
+ for (i = 0; i < Opts_MaxComets(); i++)
{
comets[i].alive = 0;
comets[i].expl = -1;
@@ -3416,13 +3853,12 @@
comets[i].x = 0;
comets[i].y = 0;
comets[i].answer = 0;
-// strncpy(comets[i].flashcard.formula_string, " ", max_formula_size);
-// strncpy(comets[i].flashcard.answer_string, " ", max_answer_size);
MC_ResetFlashCard(&(comets[i].flashcard) );
comets[i].bonus = 0;
}
}
+
void print_status(void)
{
int i;
@@ -3446,6 +3882,7 @@
printf("\n");
}
+
void free_on_exit(void)
{
int i;
@@ -3463,8 +3900,8 @@
int i, img;
int old_city_expl_height = city_expl_height;
- DEBUGMSG(debug_game,"Recalculating positions\n")
-
+ DEBUGMSG(debug_game,"Recalculating positions\n");
+
if (Opts_GetGlobalOpt(USE_IGLOOS))
img = IMG_IGLOO_INTACT;
else
@@ -3477,7 +3914,7 @@
{
cities[i].x = (((GetScreen()->w / (NUM_CITIES + 1)) * i) +
((GetImage(img) -> w) / 2));
- DEBUGMSG(debug_game,"%d,", cities[i].x)
+ DEBUGMSG(debug_game,"%d,", cities[i].x);
}
else
{
@@ -3494,7 +3931,7 @@
city_expl_height = GetScreen()->h - GetImage(IMG_CITY_BLUE)->h;
//move comets to a location 'equivalent' to where they were
//i.e. with the same amount of time left before impact
- for (i = 0; i < MAX_COMETS; ++i)
+ for (i = 0; i < Opts_MaxComets(); ++i)
{
if (!comets[i].alive)
continue;
@@ -3505,6 +3942,14 @@
//else
// comets[i].y = comets[i].y * RES_Y / screen->h;
}
+}
-
+static int num_comets_alive()
+{
+ int i = 0;
+ int living = 0;
+ for(i = 0; i < Opts_MaxComets(); i++)
+ if(comets[i].alive)
+ living++;
+ return living;
}
Modified: branches/commonification/tuxmath/trunk/src/game.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/game.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/game.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -143,8 +143,8 @@
int PrerenderGraphics();
int game(void);
void game_set_start_message(const char*, const char*, const char*, const char*);
+
/* draw_nums() is used in options.c and factoroids.c/h so need extern linkage */
-
void draw_nums(const char* str, int x, int y);
/*used in factoroids.c/h*/
Modified: branches/commonification/tuxmath/trunk/src/gettext.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/gettext.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/gettext.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -21,7 +21,6 @@
/* NLS can be disabled through the configure --disable-nls option. */
#if ENABLE_NLS
-
/* Get declarations of GNU message catalog functions. */
# include <libintl.h>
Modified: branches/commonification/tuxmath/trunk/src/globals.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/globals.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/globals.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -25,9 +25,21 @@
#ifndef GLOBALS_H
#define GLOBALS_H
+//#include "config.h"
#include "config.h"
-#ifndef HAVE_LIBT4KCOMMON
+// Translation stuff (now works for Mac and Win too!):
+#include "gettext.h"
+#include <locale.h>
+#define _(String) gettext (String)
+#define gettext_noop(String) String
+#define N_(String) gettext_noop (String)
+
+#include <wchar.h>
+
+#ifdef HAVE_LIBT4KCOMMON
+# include <t4kcommon.h>
+#else
typedef enum { false, true } bool;
#endif // HAVE_LIBT4KCOMMON
@@ -78,6 +90,7 @@
#define DEFAULT_MENU_SOUND 1
#define DEFAULT_MENU_MUSIC 1
#define DEFAULT_FULLSCREEN 1
+#define DEFAULT_LAN_MODE 0
#define DEFAULT_USE_BKGD 1
#define DEFAULT_HELP_MODE 0
#define DEFAULT_DEMO_MODE 0
@@ -116,6 +129,7 @@
#define MAX_BONUS_SPEED_RATIO 3.0
#define MIN_COMETS 1
#define MAX_MAX_COMETS 100
+#define SCORE_COEFFICIENT 100
#define DEFAULT_FONT_NAME "AndikaDesRevG.ttf"
#define FONT_NAME_LENGTH 64
Modified: branches/commonification/tuxmath/trunk/src/highscore.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/highscore.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/highscore.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -16,11 +16,13 @@
#include "menu.h"
#include "titlescreen.h"
#include "fileops.h"
-#include "fileops_media.h"
#include "setup.h"
#include "options.h"
#include "SDL_extras.h"
#include "convert_utf.h"
+#include "transtruct.h"
+#include "network.h"
+#include "throttle.h"
typedef struct high_score_entry {
@@ -313,6 +315,12 @@
DrawTitleScreen();
+ /* Red "Stop" circle in upper right corner to go back to main menu: */
+ if (stop_button)
+ {
+ SDL_BlitSurface(stop_button, NULL, screen, &stop_rect);
+ }
+
/* Draw translucent background for text: */
{
SDL_Rect bg_rect;
@@ -480,7 +488,7 @@
SDL_EnableUNICODE(SDL_DISABLE);
/* Now copy name into location pointed to by arg: */
- strncpy((char*)pl_name, (char*)UTF8_buf, HIGH_SCORE_NAME_LENGTH * 3);
+ strncpy(pl_name, UTF8_buf, HIGH_SCORE_NAME_LENGTH * 3);
}
@@ -715,3 +723,445 @@
return high_scores[diff_level][place].name;
}
+
+
+int Ready(const char* heading)
+{
+ SDL_Rect loc;
+ SDL_Rect okRect;
+ int finished = 0;
+ Uint32 frame = 0;
+ Uint32 timer = 0;
+ const int BG_Y = 100;
+ const int BG_WIDTH = 400;
+ const int BG_HEIGHT = 200;
+
+ DEBUGMSG(debug_highscore, "Enter Ready()\n" );
+
+ DrawTitleScreen();
+
+ /* Draw translucent background for text: */
+ {
+ SDL_Rect bg_rect;
+ bg_rect.x = (screen->w)/2 - BG_WIDTH/2;
+ bg_rect.y = BG_Y;
+ bg_rect.w = BG_WIDTH;
+ bg_rect.h = BG_HEIGHT;
+ DrawButton(&bg_rect, 15, REG_RGBA);
+
+ bg_rect.x += 10;
+ bg_rect.y += 10;
+ bg_rect.w -= 20;
+ bg_rect.h = 60;
+ DrawButton(&bg_rect, 10, SEL_RGBA);
+ }
+
+ /* Draw heading: */
+ {
+ SDL_Surface* s = BlackOutline(_(heading),
+ DEFAULT_MENU_FONT_SIZE, &white);
+ if (s)
+ {
+ loc.x = (screen->w/2) - (s->w/2);
+ loc.y = 110;
+ SDL_BlitSurface(s, NULL, screen, &loc);
+ SDL_FreeSurface(s);
+ }
+ }
+
+ /* Red "Stop" circle in upper right corner to go back to main menu: */
+ if (stop_button)
+ {
+ SDL_BlitSurface(stop_button, NULL, screen, &stop_rect);
+ }
+
+ /* "Next_arrow" to indicate ready to proceed: */
+ if (next_arrow)
+ {
+ okRect.x = (screen->w)/2;
+ okRect.y = 240;
+ SDL_BlitSurface(next_arrow, NULL, screen, &okRect);
+ }
+
+ /* and update: */
+ SDL_UpdateRect(screen, 0, 0, 0, 0);
+
+
+ while (!finished)
+ {
+ /* Handle user events: */
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ {
+ cleanup();
+ }
+
+ case SDL_MOUSEBUTTONDOWN:
+ /* "Stop" button - go to main menu: */
+ {
+ if (inRect(stop_rect, event.button.x, event.button.y ))
+ {
+ finished = -1;
+ playsound(SND_TOCK);
+ break;
+ }
+ else if (inRect(okRect, event.button.x, event.button.y ))
+ {
+ finished = 1;
+ playsound(SND_TOCK);
+ break;
+ }
+
+ }
+ case SDL_KEYDOWN:
+ {
+ switch (event.key.keysym.sym)
+ {
+ case SDLK_ESCAPE:
+ case SDLK_BACKSPACE:
+ {
+ finished = -2;
+ playsound(SND_TOCK);
+ break;
+ }
+ case SDLK_RETURN:
+ case SDLK_KP_ENTER:
+ case SDLK_SPACE:
+ {
+ finished = 1;
+ playsound(SND_TOCK);
+ break;
+ }
+ default:
+ {
+ //Do nothing - event. add support for toggle fullscreen, etc.
+ }
+ }
+ }
+ }
+ }
+
+ HandleTitleScreenAnimations();
+ Throttle(20, &timer);
+ frame++;
+ } // End of while (!finished) loop
+
+ DEBUGMSG(debug_highscore, "Leave Ready()\n" );
+
+ /* 1 means we start game, -1 means we go back to menu */
+ return finished;
+}
+
+
+int Standby(const char* heading, const char* sub)
+{
+ SDL_Rect loc;
+ int finished = 0;
+ Uint32 frame = 0;
+ Uint32 timer = 0;
+ const int BG_Y = 100;
+ const int BG_WIDTH = 400;
+ const int BG_HEIGHT = 200;
+
+ char buf[NET_BUF_LEN];
+
+ DEBUGMSG(debug_highscore, "Enter Standby()\n" );
+
+ DrawTitleScreen();
+
+ /* Draw translucent background for text: */
+ {
+ SDL_Rect bg_rect;
+ bg_rect.x = (screen->w)/2 - BG_WIDTH/2;
+ bg_rect.y = BG_Y;
+ bg_rect.w = BG_WIDTH;
+ bg_rect.h = BG_HEIGHT;
+ DrawButton(&bg_rect, 15, REG_RGBA);
+
+ bg_rect.x += 10;
+ bg_rect.y += 10;
+ bg_rect.w -= 20;
+ bg_rect.h = 60;
+ DrawButton(&bg_rect, 10, SEL_RGBA);
+ }
+
+ /* Draw heading: */
+ {
+ SDL_Surface* s = BlackOutline(_(heading),
+ DEFAULT_MENU_FONT_SIZE, &white);
+ if (s)
+ {
+ loc.x = (screen->w/2) - (s->w/2);
+ loc.y = 110;
+ SDL_BlitSurface(s, NULL, screen, &loc);
+ SDL_FreeSurface(s);
+ }
+
+ s = BlackOutline(_(sub),
+ DEFAULT_MENU_FONT_SIZE, &white);
+ if (s)
+ {
+ loc.x = (screen->w/2) - (s->w/2);
+ loc.y = 140;
+ SDL_BlitSurface(s, NULL, screen, &loc);
+ SDL_FreeSurface(s);
+ }
+ }
+
+ /* Red "Stop" circle in upper right corner to go back to main menu: */
+ if (stop_button)
+ {
+ SDL_BlitSurface(stop_button, NULL, screen, &stop_rect);
+ }
+
+ /* and update: */
+ SDL_UpdateRect(screen, 0, 0, 0, 0);
+
+
+ while (!finished)
+ {
+ /* Handle user events: */
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ {
+ cleanup();
+ }
+
+ case SDL_MOUSEBUTTONDOWN:
+ /* "Stop" button - go to main menu: */
+ {
+ if (inRect(stop_rect, event.button.x, event.button.y ))
+ {
+ finished = 1;
+ playsound(SND_TOCK);
+ break;
+ }
+ }
+ case SDL_KEYDOWN:
+ {
+ switch (event.key.keysym.sym)
+ {
+ case SDLK_ESCAPE:
+ case SDLK_BACKSPACE:
+ {
+ finished = -2;
+ playsound(SND_TOCK);
+ break;
+ }
+
+ default:
+ {
+ //Do nothing - event. add support for toggle fullscreen, etc.
+ }
+ }
+ }
+ }
+ }
+
+ /* Handle server messages: */
+ while(!check_messages(buf))
+ {
+ if(strncmp(buf,"GO_TO_GAME", strlen("GO_TO_GAME")) == 0)
+ {
+ finished = 1;
+ playsound(SND_TOCK);
+ break;
+ }
+ else if(strncmp(buf, "GAME_IN_PROGRESS", strlen("GAME_IN_PROGRESS")) == 0)
+ {
+ finished = -1;
+ playsound(SND_TOCK);
+ break;
+ }
+ else
+ {
+ DEBUGMSG(debug_highscore, "Unrecognized message from server: %s\n", buf);
+ continue;
+ }
+ }
+
+ HandleTitleScreenAnimations();
+ Throttle(20, &timer);
+ frame++;
+ } // End of while (!finished) loop
+
+ DEBUGMSG(debug_highscore, "Leave Standby()\n" );
+
+ /* 1 means we start game, -1 means we go back to menu */
+ return finished;
+}
+
+
+int detecting_servers(const char* heading, const char* sub)
+{
+#ifndef HAVE_LIBSDL_NET
+ return 0;
+#else
+ SDL_Rect loc;
+ SDL_Rect TuxRect,
+ stopRect;
+
+ int finished = 0;
+ int tux_frame = 0;
+ Uint32 frame = 0;
+ Uint32 start = 0;
+ Uint32 timer = 0;
+ int servers_found = 0;
+ sprite* Tux = NULL;
+
+ DEBUGMSG(debug_lan, "\nEnter detecting_servers()\n");
+
+ /* FIXME it takes several seconds to load this sprite on a */
+ /* Dell Mini 9 netbook, probably longer on many school */
+ /* machines. Perhaps we should load this ahead of time... */
+ Tux = LoadSprite("tux/bigtux", IMG_ALPHA);
+
+ /* We need to get Unicode vals from SDL keysyms */
+ SDL_EnableUNICODE(SDL_ENABLE);
+
+ /* Draw background: */
+ if (current_bkg())
+ SDL_BlitSurface(current_bkg(), NULL, screen, NULL);
+
+ /* Red "Stop" circle in upper right corner to go back to main menu: */
+ if (images[IMG_STOP])
+ {
+ stopRect.w = images[IMG_STOP]->w;
+ stopRect.h = images[IMG_STOP]->h;
+ stopRect.x = screen->w - images[IMG_STOP]->w;
+ stopRect.y = 0;
+ SDL_BlitSurface(images[IMG_STOP], NULL, screen, &stopRect);
+ }
+
+ if (Tux && Tux->frame[0]) /* make sure sprite has at least one frame */
+ {
+ TuxRect.w = Tux->frame[0]->w;
+ TuxRect.h = Tux->frame[0]->h;
+ TuxRect.x = 0;
+ TuxRect.y = screen->h - Tux->frame[0]->h;
+ }
+
+ /* Draw heading: */
+ {
+ SDL_Surface* s = BlackOutline(_(heading),
+ DEFAULT_MENU_FONT_SIZE, &white);
+ if (s)
+ {
+ loc.x = (screen->w/2) - (s->w/2);
+ loc.y = 110;
+ SDL_BlitSurface(s, NULL, screen, &loc);
+ SDL_FreeSurface(s);
+ }
+
+ s = BlackOutline(_(sub),
+ DEFAULT_MENU_FONT_SIZE, &white);
+ if (s)
+ {
+ loc.x = (screen->w/2) - (s->w/2);
+ loc.y = 140;
+ SDL_BlitSurface(s, NULL, screen, &loc);
+ SDL_FreeSurface(s);
+ }
+ }
+
+ /* and update: */
+ SDL_UpdateRect(screen, 0, 0, 0, 0);
+
+ while (!finished)
+ {
+ start = SDL_GetTicks();
+
+ //Scan local network to find running server:
+ servers_found = LAN_DetectServers();
+ if(servers_found < 1)
+ {
+ printf("No server could be found - returning.\n");
+ /* Turn off SDL Unicode lookup (because has some overhead): */
+ SDL_EnableUNICODE(SDL_DISABLE);
+ FreeSprite(Tux);
+ return 0;
+ }
+ 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("LAN_AutoSetup() failed - returning.\n");
+ /* Turn off SDL Unicode lookup (because has some overhead): */
+ SDL_EnableUNICODE(SDL_DISABLE);
+ FreeSprite(Tux);
+ return 0;
+ }
+
+
+ finished = 1;
+ break; //So we quit scanning as soon as we connect
+ printf("connected\n");
+ } else if (servers_found > 1)
+ {
+ //TODO display list of servers for player to choose from:
+ }
+
+
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ {
+ cleanup();
+ }
+
+ case SDL_MOUSEBUTTONDOWN:
+ /* "Stop" button - go to main menu: */
+ {
+ if (inRect(stopRect, event.button.x, event.button.y ))
+ {
+ finished = 1;
+ playsound(SND_TOCK);
+ break;
+ }
+ }
+ }
+ }
+
+ /* --- make tux blink --- */
+ switch (frame % TUX6)
+ {
+ case 0: tux_frame = 1; break;
+ case TUX1: tux_frame = 2; break;
+ case TUX2: tux_frame = 3; break;
+ case TUX3: tux_frame = 4; break;
+ case TUX4: tux_frame = 3; break;
+ case TUX5: tux_frame = 2; break;
+ default: tux_frame = 0;
+ }
+
+ if (Tux && tux_frame)
+ {
+ SDL_BlitSurface(Tux->frame[tux_frame - 1], NULL, screen, &TuxRect);
+ SDL_UpdateRect(screen, TuxRect.x, TuxRect.y, TuxRect.w, TuxRect.h);
+ }
+
+ /* Wait so we keep frame rate constant: */
+ Throttle(20, &timer);
+ frame++;
+ } // End of while (!finished) loop
+
+
+ /* Turn off SDL Unicode lookup (because has some overhead): */
+ SDL_EnableUNICODE(SDL_DISABLE);
+ FreeSprite(Tux);
+
+ return 1;
+
+#endif
+}
+
Modified: branches/commonification/tuxmath/trunk/src/highscore.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/highscore.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/highscore.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -16,9 +16,15 @@
#include "globals.h"
+
void DisplayHighScores(int level);
void HighScoreNameEntry(char* pl_name);
void NameEntry(char* pl_name, const char* heading, const char* sub);
+/* FIXME the next three don't have anything to do with high scores */
+/* and don't really belong here: */
+int Standby(const char* heading, const char* sub);
+int detecting_servers(const char* heading, const char* sub);
+int Ready(const char* heading);
int check_score_place(int diff_level, int new_score);
int insert_score(char* playername, int diff_level, int new_score);
Deleted: branches/commonification/tuxmath/trunk/src/mathcards.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/mathcards.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/mathcards.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,2438 +0,0 @@
-/*
-* C Implementation: mathcards.c
-*
-* Description: implementation of backend for a flashcard-type math game.
- Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
- (aka tuxmath). (If tuxmath were a C++ program, this would be a C++ class).
- MathCards could be used as the basis for similar games using a different interface.
-
-*
-*
-* Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2005
-*
-* Copyright: See COPYING file that comes with this distribution. (Briefly, GNU GPL).
-*
-* Revised extensively in 2008 by Brendan Luchen, Tim Holy, and David Bruce
-*
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-
-#include "globals.h"
-#include "mathcards.h"
-
-/* extern'd constants */
-
-const char* const MC_OPTION_TEXT[NOPTS+1] = {
-"PLAY_THROUGH_LIST",
-"QUESTION_COPIES",
-"REPEAT_WRONGS",
-"COPIES_REPEATED_WRONGS",
-"ALLOW_NEGATIVES",
-"MAX_ANSWER",
-"MAX_QUESTIONS",
-"MAX_FORMULA_NUMS",
-"MIN_FORMULA_NUMS",
-
-"FORMAT_ANSWER_LAST",
-"FORMAT_ANSWER_FIRST",
-"FORMAT_ANSWER_MIDDLE",
-"FORMAT_ADD_ANSWER_LAST",
-"FORMAT_ADD_ANSWER_FIRST",
-"FORMAT_ADD_ANSWER_MIDDLE",
-"FORMAT_SUB_ANSWER_LAST",
-"FORMAT_SUB_ANSWER_FIRST",
-"FORMAT_SUB_ANSWER_MIDDLE",
-"FORMAT_MULT_ANSWER_LAST",
-"FORMAT_MULT_ANSWER_FIRST",
-"FORMAT_MULT_ANSWER_MIDDLE",
-"FORMAT_DIV_ANSWER_LAST",
-"FORMAT_DIV_ANSWER_FIRST",
-"FORMAT_DIV_ANSWER_MIDDLE",
-
-"ADDITION_ALLOWED",
-"SUBTRACTION_ALLOWED",
-"MULTIPLICATION_ALLOWED",
-"DIVISION_ALLOWED",
-"TYPING_PRACTICE_ALLOWED",
-"ARITHMETIC_ALLOWED",
-"COMPARISON_ALLOWED",
-
-"MIN_AUGEND",
-"MAX_AUGEND",
-"MIN_ADDEND",
-"MAX_ADDEND",
-
-"MIN_MINUEND",
-"MAX_MINUEND",
-"MIN_SUBTRAHEND",
-"MAX_SUBTRAHEND",
-
-"MIN_MULTIPLIER",
-"MAX_MULTIPLIER",
-"MIN_MULTIPLICAND",
-"MAX_MULTIPLICAND",
-
-"MIN_DIVISOR",
-"MAX_DIVISOR",
-"MIN_QUOTIENT",
-"MAX_QUOTIENT",
-
-"MIN_TYPING_NUM",
-"MAX_TYPING_NUM",
-
-"MIN_COMPARATOR" ,
-"MAX_COMPARATOR" ,
-"MIN_COMPARISAND",
-"MAX_COMPARISAND",
-
-"RANDOMIZE",
-
-"COMPREHENSIVE",
-"AVG_LIST_LENGTH",
-"VARY_LIST_LENGTH",
-
-"END_OF_OPTS"
-};
-
-const int MC_DEFAULTS[] = {
- 1, //PLAY_THROUGH_LIST
- 1, //QUESTION_COPIES
- 1, //REPEAT_WRONGS
- 1, //COPIES_REPEATED_WRONGS
- 0, //ALLOW_NEGATIVES
- 999, //MAX_ANSWER
- 5000, //MAX_QUESTIONS
- 2, //MAX_FORMULA_NUMS
- 2, //MIN_FORMULA_NUMS
- //
- 1, //FORMAT_ANSWER_LAST
- 0, //FORMAT_ANSWER_FIRST
- 0, //FORMAT_ANSWER_MIDDLE
- 1, //FORMAT_ADD_ANSWER_LAST
- 0, //FORMAT_ADD_ANSWER_FIRST
- 0, //FORMAT_ADD_ANSWER_MIDDLE
- 1, //FORMAT_SUB_ANSWER_LAST
- 0, //FORMAT_SUB_ANSWER_FIRST
- 0, //FORMAT_SUB_ANSWER_MIDDLE
- 1, //FORMAT_MULT_ANSWER_LAST
- 0, //FORMAT_MULT_ANSWER_FIRST
- 0, //FORMAT_MULT_ANSWER_MIDDLE
- 1, //FORMAT_DIV_ANSWER_LAST
- 0, //FORMAT_DIV_ANSWER_FIRST
- 0, //FORMAT_DIV_ANSWER_MIDDLE
- //
- 1, //ADDITION_ALLOWED
- 1, //SUBTRACTION_ALLOWED
- 1, //MULTIPLICATION_ALLOWED
- 1, //DIVISION_ALLOWED
-
- 0, //TYPING_PRACTICE_ALLOWED
- 1, //ARITHMETIC_ALLOWED
- 0, //COMPARISON_ALLOWED
- //
- 0, //MIN_AUGEND
- 12, //MAX_AUGEND
- 0, //MIN_ADDEND
- 12, //MAX_ADDEND
- //
- 0, //MIN_MINUEND
- 12, //MAX_MINUEND
- 0, //MIN_SUBTRAHEND
- 12, //MAX_SUBTRAHEND
- //
- 0, //MIN_MULTIPLIER
- 12, //MAX_MULTIPLIER
- 0, //MIN_MULTIPLICAND
- 12, //MAX_MULTIPLICAND
- //
- 0, //MIN_DIVISOR
- 12, //MAX_DIVISOR
- 0, //MIN_QUOTIENT
- 12, //MAX_QUOTIENT
- //
- 0, //MIN_TYPING_NUM
- 12, //MAX_TYPING_NUM
- //
- 0, //MIN_COMPARATOR
- 12, //MAX_COMPARATOR
- 0, //MIN_COMPARISAND
- 12, //MAX_COMPARISAND
-
- 1, //RANDOMIZE
-
- 0, //COMPREHENSIVE
- 100, //AVG_LIST_LENGTH
- 1 //VARY_LIST_LENGTH
-};
-
-
-
-/* "Globals" for mathcards.c: */
-#define PI_VAL 3.1415927
-#define NPRIMES 9
-const int smallprimes[NPRIMES] = {2, 3, 5 ,7, 11, 13, 17, 19, 23};
-const char operchars[4] = "+-*/";
-
-MC_Options* math_opts = 0;
-MC_MathQuestion* question_list = 0;
-MC_MathQuestion* wrong_quests = 0;
-MC_MathQuestion* next_wrong_quest = 0;
-int initialized = 0;
-int quest_list_length = 0;
-int answered_correctly = 0;
-int answered_wrong = 0;
-int questions_pending = 0;
-int unanswered = 0;
-int starting_length = 0;
-int max_formula_size = 0; //max length in chars of a flashcard's formula
-int max_answer_size = 0; //and of its answer
-
-/* For keeping track of timing data */
-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 = {NULL,NULL,0,0}; //empty card to signal error
-
-/* "private" function prototypes: */
-/* */
-/* these are for internal use by MathCards only - like */
-/* the private functions of a C++ class. Declared static */
-/* to give file scope rather than extern scope. */
-
-static MC_MathQuestion* generate_list(void);
-static void clear_negatives(void);
-//static int validate_question(int n1, int n2, int n3);
-//static MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f);
-static MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard);
-static MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node);
-static MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node);
-static MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n);
-static MC_MathQuestion* delete_list(MC_MathQuestion* list);
-//static int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy);
-static int list_length(MC_MathQuestion* list);
-static int randomize_list(MC_MathQuestion** list);
-
-int comp_randomizer(const void *a, const void *b);
-static MC_MathQuestion* pick_random(int length, MC_MathQuestion* list);
-static int compare_node(MC_MathQuestion* first, MC_MathQuestion* other);
-static int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr);
-//static int int_to_bool(int i);
-//static int sane_value(int i);
-//static int abs_value(int i);
-static int log10i(int i);
-static int floatCompare(const void *v1,const void *v2);
-
-static void print_list(FILE* fp,MC_MathQuestion* list);
-void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length);
-
-/* these functions are dead code unless compiling with debug turned on: */
-static void print_card(MC_FlashCard card);
-static void print_counters(void);
-//static MC_MathQuestion* create_node_copy(MC_MathQuestion* other);
-//static MC_FlashCard create_card_from_node(MC_MathQuestion* node);
-
-/* Functions for new mathcards architecture */
-static void free_node(MC_MathQuestion* mq); //wrapper for free() that also frees card
-static MC_FlashCard generate_random_flashcard(void);
-static MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat);
-static void copy_card(const MC_FlashCard* src, MC_FlashCard* dest); //deep copy a flashcard
-static MC_MathQuestion* allocate_node(void); //allocate space for a node
-static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b); //test for identical cards
-static int find_divisor(int a); //return a random positive divisor of a
-static int calc_num_valid_questions(void);
-static MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list);
-static MC_MathQuestion* find_node(MC_MathQuestion* list, int num);
-
-/* MC_Initialize() sets up the struct containing all of */
-/* settings regarding math questions. It should be */
-/* called before any other function. Many of the other */
-/* functions will not work properly if MC_Initialize() */
-/* has not been called. It only needs to be called once, */
-/* i.e when the program is starting, not at the beginning*/
-/* of each math game for the player. Returns 1 if */
-/* successful, 0 otherwise. */
-int MC_Initialize(void)
-{
- int i;
-
- mcdprintf("\nEntering MC_Initialize()");
- /* check flag to see if we did this already */
- if (initialized)
- {
-
- if (debug_status & 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)
- {
-
- mcdprintf("\nError: math_opts null or invalid");
- mcdprintf("\nLeaving MC_Initialize()\n");
-
- fprintf(stderr, "\nUnable to initialize math_options");
- return 0;
- }
-
- /* set defaults */
- for (i = 0; i < NOPTS; ++i)
- {
- math_opts->iopts[i] = MC_DEFAULTS[i];
- }
-
- /* if no negatives to be used, reset any negatives to 0 */
- if (!math_opts->iopts[ALLOW_NEGATIVES])
- {
- clear_negatives();
- }
-
- initialized = 1;
-
- if (debug_status & 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)
-{
- mcdprintf("\nEntering MC_StartGame()");
-
- /* if math_opts not set up yet, initialize it: */
- if (!initialized)
- {
-
- mcdprintf("\nNot initialized - calling MC_Initialize()");
-
- MC_Initialize();
- }
-
- if (!math_opts)
- {
- mcdprintf("\nCould not initialize - bailing out");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 0;
- }
- /* we know math_opts exists if we make it to here */
- srand(time(NULL));
-
- /* clear out old lists if starting another game: (if not done already) */
- delete_list(question_list);
- question_list = NULL;
- delete_list(wrong_quests);
- wrong_quests = NULL;
-
- /* 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;
- }
-
- /* determine how much space needed for strings, based on user options */
- max_formula_size = MC_GetOpt(MAX_FORMULA_NUMS)
- * (log10i(MC_GLOBAL_MAX) + 4) //sign/operator/spaces
- + 1; //question mark for answer
- max_answer_size = (int)(log10i(MC_GLOBAL_MAX) ) + 2; //negative sign + digit
-
- mcdprintf("max answer, formula size: %d, %d\n",
- max_answer_size, max_formula_size);
- /* set up new list with pointer to top: */
- question_list = generate_list();
-
- next_wrong_quest = 0;
- /* initialize counters for new game: */
- quest_list_length = list_length(question_list);
- /* Note: the distinction between quest_list_length and */
- /* unanswered is that the latter includes questions */
- /* that are currently "in play" by the user interface - */
- /* it is only decremented when an answer to the question*/
- /* is received. */
- unanswered = starting_length = quest_list_length;
- answered_correctly = 0;
- answered_wrong = 0;
- questions_pending = 0;
-
- if (debug_status & debug_mathcards) {
- print_counters();
- }
-
- /* make sure list now exists and has non-zero length: */
- if (question_list && quest_list_length)
- {
- mcdprintf("\nGame set up successfully");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 1;
- }
- else
- {
- mcdprintf("\nGame NOT set up successfully - no valid list");
- mcdprintf("\nLeaving MC_StartGame()\n");
-
- return 0;
- }
-}
-
-/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
-/* but uses the incorrectly answered questions from the */
-/* previous game for the question list as a review form */
-/* of learning. If there were no wrong answers (or no */
-/* previous game), it behaves just like MC_StartGame(). */
-/* FIXME wonder if it should return a different value if */
-/* the list is created from settings because there is no */
-/* valid wrong question list? */
-int MC_StartGameUsingWrongs(void)
-{
- mcdprintf("\nEntering MC_StartGameUsingWrongs()");
-
- /* Note: if not initialized, control will pass to */
- /* MC_StartGame() via else clause so don't need to test */
- /* for initialization here */
- if (wrong_quests &&
- list_length(wrong_quests))
- {
- mcdprintf("\nNon-zero length wrong_quests list found, will");
- mcdprintf("\nuse for new game list:");
-
- /* initialize lists for new game: */
- delete_list(question_list);
- if(!randomize_list(&wrong_quests)) {
- fprintf(stderr, "Error during randomization of wrong_quests!\n");
- /* Punt on trying wrong question list, just run normal game */
- return MC_StartGame();
- }
- question_list = wrong_quests;
- wrong_quests = 0;
- next_wrong_quest = 0;
- /* 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 */
- {
- mcdprintf("\nNo wrong questions to review - generate list from math_opts\n");
- mcdprintf("\nLeaving MC_StartGameUsingWrongs()\n");
-
- return MC_StartGame();
- }
-}
-
-
-/* MC_NextQuestion() takes a pointer to an allocated */
-/* MC_MathQuestion struct and fills in the fields for */
-/* use by the user interface program. It basically is */
-/* like taking the next flashcard from the pile. The */
-/* node containing the question is removed from the list.*/
-/* Returns 1 if question found, 0 if list empty/invalid */
-/* or if argument pointer is invalid. */
-int MC_NextQuestion(MC_FlashCard* fc)
-{
- mcdprintf("\nEntering MC_NextQuestion()\n");
-
- /* (so we can free the node after removed from list:) */
- MC_MathQuestion* ptr;
- ptr = question_list;
-
- if (!fc )
- {
- fprintf(stderr, "\nNull MC_FlashCard* argument!\n");
- mcdprintf("\nLeaving MC_NextQuestion()\n");
- return 0;
- }
-
- if (!question_list ||
-/* !next_question || */
- !list_length(question_list) )
- {
- mcdprintf("\nquestion_list invalid or empty");
- mcdprintf("\nLeaving MC_NextQuestion()\n");
-
- return 0;
- }
-
- /* 'draw' - copy over the first question */
- copy_card(&question_list->card, fc);
-
- /* 'discard' - take first question node out of list and free it */
- question_list = remove_node(question_list, question_list);
- free_node(ptr);
- quest_list_length--;
- questions_pending++;
-
- 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 1 if no errors. */
-int MC_AnsweredCorrectly(MC_FlashCard* fc)
-{
- mcdprintf("\nEntering MC_AnsweredCorrectly()");
-
- if (!fc)
- {
- fprintf(stderr, "\nMC_AnsweredCorrectly() passed invalid pointer as argument!\n");
-
- mcdprintf("\nInvalid MC_FlashCard* argument!");
- mcdprintf("\nLeaving MC_AnsweredCorrectly()\n");
-
- return 0;
- }
-
- if (debug_status & debug_mathcards) {
- printf("\nQuestion was:");
- print_card(*fc);
- }
-
- answered_correctly++;
- questions_pending--;
-
- if (!math_opts->iopts[PLAY_THROUGH_LIST])
- /* reinsert question into question list at random location */
- {
- mcdprintf("\nReinserting question into list");
-
- MC_MathQuestion* ptr1;
- MC_MathQuestion* ptr2;
- /* make new node using values from flashcard */
- ptr1 = create_node_from_card(fc);
- /* put it into list */
- ptr2 = pick_random(quest_list_length, question_list);
- question_list = insert_node(question_list, ptr2, ptr1);
- quest_list_length++;
- /* unanswered does not change - was not decremented when */
- /* question allocated! */
- }
- else
- {
- mcdprintf("\nNot reinserting question into list");
- /* not recycling questions so fewer questions remain: */
- unanswered--;
- }
-
- if (debug_status & debug_mathcards) {
- print_counters();
- printf("\nLeaving MC_AnsweredCorrectly()\n");
- }
-
- return 1;
-}
-
-/* MC_NotAnsweredCorrectly() is how the user interface */
-/* tells MathCards that the player failed to answer the */
-/* question correctly. Returns 1 if no errors. */
-/* Note: this gets triggered only if a player's city */
-/* gets hit by a question, not if they "miss". */
-int MC_NotAnsweredCorrectly(MC_FlashCard* fc)
-{
- mcdprintf("\nEntering MC_NotAnsweredCorrectly()");
-
- if (!fc)
- {
- fprintf(stderr, "\nMC_NotAnsweredCorrectly() passed invalid pointer as argument!\n");
-
- mcdprintf("\nInvalid MC_FlashCard* argument!");
- mcdprintf("\nLeaving MC_NotAnsweredCorrectly()\n");
-
- return 0;
- }
-
- if (debug_status & debug_mathcards) {
- printf("\nQuestion was:");
- print_card(*fc);
- }
-
- answered_wrong++;
- questions_pending--;
-
- /* add question to wrong_quests list: */
-
- MC_MathQuestion* ptr1;
- MC_MathQuestion* ptr2;
-
- ptr1 = create_node_from_card(fc);
-
- if (!already_in_list(wrong_quests, ptr1)) /* avoid duplicates */
- {
- mcdprintf("\nAdding to wrong_quests list");
- wrong_quests = append_node(wrong_quests, ptr1);
- }
- else /* avoid memory leak */
- {
- free(ptr1);
- }
-
- /* if desired, put question back in list so student sees it again */
- if (math_opts->iopts[REPEAT_WRONGS])
- {
- int i;
-
- mcdprintf("\nAdding %d copies to question_list:", math_opts->iopts[COPIES_REPEATED_WRONGS]);
-
- /* can put in more than one copy (to drive the point home!) */
- for (i = 0; i < math_opts->iopts[COPIES_REPEATED_WRONGS]; i++)
- {
- ptr1 = create_node_from_card(fc);
- ptr2 = pick_random(quest_list_length, question_list);
- question_list = insert_node(question_list, ptr2, ptr1);
- quest_list_length++;
- }
- /* unanswered stays the same if a single copy recycled or */
- /* increases by 1 for each "extra" copy reinserted: */
- unanswered += (math_opts->iopts[COPIES_REPEATED_WRONGS] - 1);
- }
- else
- {
- mcdprintf("\nNot repeating wrong answers\n");
-
- /* not repeating questions so list gets shorter: */
- unanswered--;
- }
-
- if (debug_status & 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;
-
- /* 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";
-
- }
-
-
- mcdprintf("\nEntering MC_PrintMathOptions()\n");
-
- /* bail out if no struct */
- if (!math_opts)
- {
- fprintf(stderr, "\nMath Options struct does not exist!\n");
- return;
- }
-
- for (i = 0; i < NOPTS; ++i)
- {
- if (verbose && vcomments[i] != NULL)
- fprintf(fp, "%s", vcomments[i]);
- fprintf(fp, "%s = %d\n", MC_OPTION_TEXT[i], math_opts->iopts[i]);
- }
- mcdprintf("\nLeaving MC_PrintMathOptions()\n");
-}
-
-
-
-int MC_PrintQuestionList(FILE* fp)
-{
- if (fp && question_list)
- {
- print_list(fp, question_list);
- return 1;
- }
- else
- {
- fprintf(stderr, "\nFile pointer and/or question list invalid\n");
- return 0;
- }
-}
-
-int MC_PrintWrongList(FILE* fp)
-{
- if (!fp)
- {
- fprintf(stderr, "File pointer invalid\n");
- return 0;
- }
-
- if (wrong_quests)
- {
- print_list(fp, wrong_quests);
- }
- else
- {
- fprintf(fp, "\nNo wrong questions!\n");
- }
-
- return 1;
-}
-
-
-int MC_StartingListLength(void)
-{
- return starting_length;
-}
-
-
-int MC_WrongListLength(void)
-{
- return list_length(wrong_quests);
-}
-
-int MC_NumAnsweredCorrectly(void)
-{
- return answered_correctly;
-}
-
-
-int MC_NumNotAnsweredCorrectly(void)
-{
- return answered_wrong;
-}
-
-
-/* Report the median time per question */
-float MC_MedianTimePerQuestion(void)
-{
- if (length_time_per_question_list == 0)
- return 0;
-
- qsort(time_per_question_list,length_time_per_question_list,sizeof(float),floatCompare);
- return time_per_question_list[length_time_per_question_list/2];
-}
-
-/* Implementation of "private methods" - (cannot be called from outside
-of this file) */
-
-
-
-/* Resets negative values to zero - used when allow_negatives deselected. */
-void clear_negatives(void)
-{
- int i;
- for (i = MIN_AUGEND; i <= MAX_TYPING_NUM; ++i)
- if (math_opts->iopts[i]< 0)
- math_opts->iopts[i]= 0;
-}
-
-// /* this is used by generate_list to see if a possible question */
-// /* meets criteria to be added to the list or not: */
-// int validate_question(int n1, int n2, int n3)
-// {
-// /* make sure none of values exceeds max_answer using absolute */
-// /* value comparison: */
-// if (abs_value(n1) > abs_value(math_opts->iopts[MAX_ANSWER])
-// || abs_value(n2) > abs_value(math_opts->iopts[MAX_ANSWER])
-// || abs_value(n3) > abs_value(math_opts->iopts[MAX_ANSWER]))
-// {
-// return 0;
-// }
-// /* make sure none of values are negative if negatives not allowed: */
-// if (!math_opts->iopts[ALLOW_NEGATIVES])
-// {
-// if (n1 < 0 || n2 < 0 || n3 < 0)
-// {
-// return 0;
-// }
-// }
-// return 1;
-// }
-
-#if 0 //this code is probably on the way out...
-/* create a new node and return a pointer to it */
-MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f)
-{
- MC_MathQuestion* ptr = NULL;
-
- ptr = (MC_MathQuestion*)malloc(sizeof(MC_MathQuestion));
-
- if (!ptr)
- {
- fprintf(stderr, "create_node() - malloc() failed!\n");
- return NULL;
- }
-
- ptr->card = MC_AllocateFlashcard();
- ptr->next = NULL;
- ptr->previous = NULL;
-
- snprintf(ptr->card.formula_string, max_formula_size, "%d %c %d = ?",
- n1, op < MC_NUM_OPERS ? operchars[op] : '\0', n2);
- snprintf(ptr->card.answer_string, max_formula_size, "%d", ans);
- ptr->card.difficulty = 25 * (op + 1);
-
-
- /* ptr should now point to a properly constructed node: */
- return ptr;
-}
-#endif
-
-MC_MathQuestion* create_node_from_card(const MC_FlashCard* flashcard)
-{
- MC_MathQuestion* ret = allocate_node();
- copy_card(flashcard, &(ret->card));
- return ret;
-}
-
-// /* FIXME take care of strings */
-// /* this one copies the contents, including pointers; both nodes must be allocated */
-// int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy)
-// {
-// if (!original)
-// {
-// fprintf(stderr, "\nIn copy_node(): invalid 'original' pointer arg.\n");
-// return 0;
-// }
-// if (!copy)
-// {
-// fprintf(stderr, "\nIn copy_node(): invalid 'copy' pointer arg.\n");
-// return 0;
-// }
-//
-// copy_card(&(original->card), &(copy->card) );
-//
-// copy->next = original->next;
-// copy->previous = original->previous;
-// copy->randomizer = original->randomizer;
-// return 1;
-// }
-
-
-
-
-/* this puts the node into the list AFTER the node pointed to by current */
-/* and returns a pointer to the top of the modified list */
-MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node)
-{
- /* return pointer to list unchanged if new_node doesn't exist*/
- if (!new_node)
- return first;
- /* if current doesn't exist, new_node is first */
- if (!current)
- {
- new_node->previous = 0;
- new_node->next =0;
- first = new_node;
- return first;
- }
-
- if (current->next) /* avoid error if at end of list */
- current->next->previous = new_node;
- new_node->next = current->next;
- current->next = new_node;
- new_node->previous = current;
- return first;
-}
-
-
-
-/* adds the new node to the end of the list */
-MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node)
-{
- MC_MathQuestion* ptr;
- /* return pointer to list unchanged if new_node doesn't exist*/
- if (!new_node)
- {
- return list;
- }
-
- /* if list does not exist, new_node is the first (and only) node */
- if (!list)
- {
- return new_node;
- }
- /* otherwise, go to end of list */
- ptr = list;
- while (ptr->next)
- {
- ptr = ptr->next;
- }
-
- ptr->next = new_node;
- new_node->previous = ptr;
- new_node->next = 0;
- return list;
-}
-
-
-
-/* this takes the node out of the list but does not delete it */
-/* and returns a pointer to the top of the modified list */
-MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n)
-{
- if (!n || !first)
- return first;
- /* special case if first node being removed */
- if (n == first)
- first = first->next;
-
- if (n->previous)
- n->previous->next = n->next;
- if (n->next)
- n->next->previous = n->previous;
- n->previous = 0;
- n->next = 0;
- return first;
-}
-
-
-
-/* frees memory for entire list and returns null pointer */
-MC_MathQuestion* delete_list(MC_MathQuestion* list)
-{
- MC_MathQuestion* tmp_ptr;
- while (list)
- {
- tmp_ptr = list->next;
- free_node (list);
- list = tmp_ptr;
- }
- return list;
-}
-
-
-
-void print_list(FILE* fp, MC_MathQuestion* list)
-{
- if (!list)
- {
- fprintf(stderr, "\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;
- mcdprintf("Entering print_vect_list()\n");
- for(i = 0; i < length; i++)
- fprintf(fp, "%s\n", vect[i]->card.formula_string);
-
- mcdprintf("Leaving print_vect_list()\n");
-}
-
-void print_card(MC_FlashCard card)
-{
- printf("\nprint_card():");
- printf("formula_string = %s\nanswer_string = %s\ndifficulty = %d\n\n",
- card.formula_string,
- card.answer_string,
- card.difficulty);
-}
-
-/* This sends the values of all "global" counters and the */
-/* lengths of the question lists to stdout - for debugging */
-void print_counters(void)
-{
- printf("\nquest_list_length = \t%d", quest_list_length);
- printf("\nlist_length(question_list) = \t%d", list_length(question_list));
- printf("\nstarting_length = \t%d", starting_length);
- printf("\nunanswered = \t%d", unanswered);
- printf("\nanswered_correctly = \t%d", answered_correctly);
- printf("\nanswered_wrong = \t%d", answered_wrong);
- printf("\nlist_length(wrong_quests) = \t%d", list_length(wrong_quests));
- printf("\nquestions_pending = \t%d", questions_pending);
-}
-
-// /* a "copy constructor", so to speak */
-// /* FIXME should properly return newly allocated list if more than one node DSB */
-// MC_MathQuestion* create_node_copy(MC_MathQuestion* other)
-// {
-// MC_MathQuestion* ret = allocate_node();
-// if (ret)
-// copy_card(&(other->card), &(ret->card) );
-// return ret;
-// }
-//
-// /* FIXME take care of strings */
-//
-// MC_FlashCard create_card_from_node(MC_MathQuestion* node)
-// {
-// MC_FlashCard fc;
-// if (!node)
-// return DEFAULT_CARD;
-// fc = MC_AllocateFlashcard();
-// copy_card(&(node->card), &fc);
-// return fc;
-// }
-
-int list_length(MC_MathQuestion* list)
-{
- int length = 0;
- while (list)
- {
- length++;
- list = list->next;
- }
- return length;
-}
-
-
-
-
-
-
-/* This is a new implementation written in an attempt to avoid */
-/* the O(n^2) performance problems seen with the old randomization */
-/* function. The list is created as a vector, but is for now still */
-/* made a linked list to minimize changes needed elsewhere. */
-/* The argument is a pointer to the top of the old list. This extra */
-/* level of indirection allows the list to be shuffled "in-place". */
-/* The function returns 1 if successful, 0 on errors. */
-
-static int randomize_list(MC_MathQuestion** old_list)
-{
- MC_MathQuestion* old_tmp = *old_list;
- MC_MathQuestion** tmp_vect = NULL;
-
- int i = 0;
- if (!old_list || !*old_list) //invalid/empty list
- return 0;
-
- int old_length = list_length(old_tmp);
-
- /* set random seed: */
- srand(time(0));
-
-
- /* Allocate vector and set ptrs to nodes in old list: */
-
- /* Allocate a list of pointers, not space for the nodes themselves: */
- tmp_vect = (MC_MathQuestion**)malloc(sizeof(MC_MathQuestion*) * old_length);
- /* Set each pointer in the vector to the corresponding node: */
- for (i = 0; i < old_length; i++)
- {
- tmp_vect[i] = old_tmp;
- tmp_vect[i]->randomizer = rand();
- old_tmp = old_tmp->next;
- }
-
- /* Now simply sort on 'tmp_vect[i]->randomizer' to shuffle list: */
- qsort(tmp_vect, old_length,
- sizeof(MC_MathQuestion*),
- comp_randomizer);
-
- /* Re-create pointers to provide linked-list functionality: */
- /* (stop at 'old_length-1' because we dereference tmp_vect[i+1]) */
- for(i = 0; i < old_length - 1; i++)
- {
- if (!tmp_vect[i])
- {
- fprintf(stderr, "Invalid pointer!\n");
- return 0;
- }
- tmp_vect[i]->next = tmp_vect[i+1];
- tmp_vect[i+1]->previous = tmp_vect[i];
- }
- /* Handle end cases: */
- tmp_vect[0]->previous = NULL;
- tmp_vect[old_length-1]->next = NULL;
-
- /* Now arrange for arg pointer to indirectly point to first element! */
- *old_list = tmp_vect[0];
- free(tmp_vect);
- return 1;
-}
-
-
-
-/* This is needed for qsort(): */
-int comp_randomizer (const void* a, const void* b)
-{
-
- int int1 = (*(const struct MC_MathQuestion **) a)->randomizer;
- int int2 = (*(const struct MC_MathQuestion **) b)->randomizer;
-
- if (int1 > int2)
- return 1;
- else if (int1 == int2)
- return 0;
- else
- return -1;
-}
-
-MC_MathQuestion* pick_random(int length, MC_MathQuestion* list)
-{
- int i;
- int rand_node;
-
- /* set random seed DSB */
- srand(time(0));
-
- /* if length is zero, get out to avoid divide-by-zero error */
- if (0 == length)
- {
- return list;
- }
-
- rand_node = rand() % length;
-
- for (i=1; i < rand_node; i++)
- {
- if (list)
- list = list->next;
- }
-
- return list;
-}
-
-/* compares fields other than pointers */
-int compare_node(MC_MathQuestion* first, MC_MathQuestion* other)
-{
- if (!first || !other)
- return 0;
- if (compare_card(&(first->card), &(first->card) ) ) //cards are equal
- return 1;
- else
- return 0;
-}
-
-/* check to see if list already contains an identical node */
-int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr)
-{
- if (!list || !ptr)
- return 0;
-
- while (list)
- {
- if (compare_node(list, ptr))
- return 1;
- list = list->next;
- }
- return 0;
-}
-
-// /* to prevent option settings in math_opts from getting set to */
-// /* values other than 0 or 1 */
-// int int_to_bool(int i)
-// {
-// if (i)
-// return 1;
-// else
-// return 0;
-// }
-
-// /* prevent values from getting into math_opts that are outside */
-// /* the range that can be handled by the program (i.e. more */
-// /* than three digits; also disallow negatives if that has been */
-// /* selected. */
-// int sane_value(int i)
-// {
-// if (i > MC_GLOBAL_MAX)
-// i = MC_GLOBAL_MAX;
-// else if (i < -MC_GLOBAL_MAX)
-// i = -MC_GLOBAL_MAX;
-//
-// if (i < 0
-// && math_opts
-// && !math_opts->iopts[ALLOW_NEGATIVES])
-// {
-// i = 0;
-// }
-//
-// return i;
-// }
-
-// int abs_value(int i)
-// {
-// if (i > 0)
-// return i;
-// else
-// return -i;
-// }
-
-int log10i(int i) //base 10 logarithm for ints
-{
- int j;
- for (j = 0; i > 0; i /= 10, ++j);
- return j;
-}
-
-/* Compares two floats (needed for sorting in MC_MedianTimePerQuestion) */
-int floatCompare(const void *v1,const void *v2)
-{
- float f1,f2;
-
- f1 = *((float *) v1);
- f2 = *((float *) v2);
-
- if (f1 < f2)
- return -1;
- else if (f1 > f2)
- return 1;
- else
- return 0;
-}
-
-
-
-/****************************************************
-Functions for new mathcards architecture
-****************************************************/
-
-void copy_card(const MC_FlashCard* src, MC_FlashCard* dest)
-{
- if (!src || !dest)
- return;
- mcdprintf("Copying '%s' to '%s', ", src->formula_string,dest->formula_string);
- mcdprintf("copying '%s' to '%s'\n", src->answer_string, dest->answer_string);
- strncpy(dest->formula_string, src->formula_string, max_formula_size);
- strncpy(dest->answer_string, src->answer_string, max_answer_size);
- mcdprintf("Card is: '%s', '%s'\n", dest->formula_string, dest->answer_string);
- dest->answer = src->answer;
- dest->difficulty = src->difficulty;
-}
-
-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;
-
- mcdprintf("Entering generate_random_flashcard()\n");
-
- do
- pt = rand() % MC_NUM_PTYPES;
- while ( (pt == MC_PT_TYPING && !MC_GetOpt(TYPING_PRACTICE_ALLOWED) ) ||
- (pt == MC_PT_ARITHMETIC && !MC_GetOpt(ADDITION_ALLOWED) &&
- !MC_GetOpt(SUBTRACTION_ALLOWED) &&
- !MC_GetOpt(MULTIPLICATION_ALLOWED) &&
- !MC_GetOpt(DIVISION_ALLOWED) ) ||
- (pt == MC_PT_COMPARISON && !MC_GetOpt(COMPARISON_ALLOWED) )
- );
-
- if (pt == MC_PT_TYPING) //typing practice
- {
- mcdprintf("Generating typing question\n");
- ret = MC_AllocateFlashcard();
- num = rand() % (MC_GetOpt(MAX_TYPING_NUM)-MC_GetOpt(MIN_TYPING_NUM) + 1)
- + MC_GetOpt(MIN_TYPING_NUM);
- snprintf(ret.formula_string, max_formula_size, "%d", num);
- snprintf(ret.answer_string, max_answer_size, "%d", num);
- ret.answer = num;
- ret.difficulty = 10;
- }
- else //if (pt == MC_PT_ARITHMETIC)
- {
- mcdprintf("Generating arithmetic question");
- length = rand() % (MC_GetOpt(MAX_FORMULA_NUMS) -
- MC_GetOpt(MIN_FORMULA_NUMS) + 1) //avoid div by 0
- + MC_GetOpt(MIN_FORMULA_NUMS);
- mcdprintf(" of length %d", length);
- ret = generate_random_ooo_card_of_length(length, 1);
- if (debug_status & debug_mathcards) {
- print_card(ret);
- }
- }
- //TODO comparison problems (e.g. "6 ? 9", "<")
-
- mcdprintf("Exiting generate_random_flashcard()\n");
-
- return ret;
-}
-
-/*
-Recursively generate an order of operations problem. Hopefully this won't
-raise performance issues. Difficulty is calculated based on the length of
-the formula and on the operators used. Problems have a 'base' difficulty of
-1 for binary operations, 3 for 3 numbers, 6, 10, etc. Each operator adds to
-the score: 0, 1, 2, and 3 respectively for addition, subtraction,
-multiplication and division.If reformat is 0, FORMAT_ANS_LAST will be used,
-otherwise a format is chosen at random.
-*/
-MC_FlashCard generate_random_ooo_card_of_length(int length, int reformat)
-{
- int format = 0;
- int r1 = 0;
- int r2 = 0;
- int ans = 0;
- char tempstr[max_formula_size];
- MC_FlashCard ret;
- MC_Operation op;
-
- printf(".");
- if (length > MAX_FORMULA_NUMS)
- return DEFAULT_CARD;
- if (length <= 2)
- {
- mcdprintf("\n");
- ret = MC_AllocateFlashcard();
- for (op = rand() % MC_NUM_OPERS; //pick a random operation
- MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
- op = rand() % MC_NUM_OPERS);
-
- mcdprintf("Operation is %c\n", operchars[op]);
- /*
- if (op == MC_OPER_ADD)
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
- r2 = rand() % (math_opts->iopts[MAX_ADDEND] - math_opts->iopts[MIN_ADDEND] + 1) + math_opts->iopts[MIN_ADDEND];
- ans = r1 + r2;
- }
- else if (op == MC_OPER_SUB)
- {
- r1 = rand() % (math_opts->iopts[MAX_MINUEND] - math_opts->iopts[MIN_MINUEND] + 1) + math_opts->iopts[MIN_MINUEND];
- r2 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
- ans = r1 - r2;
- }
- else if (op == MC_OPER_MULT)
- {
- r1 = rand() % (math_opts->iopts[MAX_MULTIPLIER] - math_opts->iopts[MIN_MULTIPLIER] + 1) + math_opts->iopts[MIN_MULTIPLIER];
- r2 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_MULTIPLICAND];
- ans = r1 * r2;
- }
- else if (op == MC_OPER_DIV)
- {
- ans = rand() % (math_opts->iopts[MAX_QUOTIENT] - math_opts->iopts[MIN_QUOTIENT] + 1) + math_opts->iopts[MIN_QUOTIENT];
- r2 = rand() % (math_opts->iopts[MAX_DIVISOR] - math_opts->iopts[MIN_DIVISOR] + 1) + math_opts->iopts[MIN_DIVISOR];
- if (r2 == 0)
- r2 = 1;
- r1 = ans * r2;
- }
- */
- if (op > MC_OPER_DIV || op < MC_OPER_ADD)
- {
- mcdprintf("Invalid operator: value %d\n", op);
- return DEFAULT_CARD;
- }
- //choose two numbers in the proper range and get their result
-
- else do
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND+4*op] - math_opts->iopts[MIN_AUGEND+4*op] + 1) + math_opts->iopts[MIN_AUGEND+4*op];
- r2 = rand() % (math_opts->iopts[MAX_ADDEND+4*op] - math_opts->iopts[MIN_ADDEND+4*op] + 1) + math_opts->iopts[MIN_ADDEND+4*op];
-
- if (op == MC_OPER_ADD)
- ans = r1 + r2;
- if (op == MC_OPER_SUB)
- ans = r1 - r2;
- if (op == MC_OPER_MULT)
- ans = r1 * r2;
- if (op == MC_OPER_DIV)
- {
- if (r2 == 0)
- r2 = 1;
- ret.difficulty = r1;
- r1 *= r2;
- ans = ret.difficulty;
- }
- } while ( (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES)) || ans > MC_GetOpt(MAX_ANSWER) );
-
-
- mcdprintf("Constructing answer_string\n");
- snprintf(ret.answer_string, max_answer_size+1, "%d", ans);
- mcdprintf("Constructing formula_string\n");
- snprintf(ret.formula_string, max_formula_size, "%d %c %d",
- r1, operchars[op], r2);
- ret.answer = ans;
- ret.difficulty = op + 1;
-
- }
- else //recurse
- {
- ret = generate_random_ooo_card_of_length(length - 1, 0);
-
- if (strchr(ret.formula_string, '+') || strchr(ret.formula_string, '-') )
- {
- //if the expression has addition or subtraction, we can't assume that
- //introducing multiplication or division will produce a predictable
- //result, so we'll limit ourselves to more addition/subtraction
- for (op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB;
- MC_GetOpt(op + ADDITION_ALLOWED) == 0;
- op = rand() % 2 ? MC_OPER_ADD : MC_OPER_SUB);
-
- }
- else
- {
- //the existing expression can be treated as a number in itself, so we
- //can do anything to it and be confident of the result.
- for (op = rand() % MC_NUM_OPERS; //pick a random operation
- MC_GetOpt(op + ADDITION_ALLOWED) == 0; //make sure it's allowed
- op = rand() % MC_NUM_OPERS);
- }
- mcdprintf("Next operation is %c,", operchars[op]);
-
- //pick the next operand
- if (op == MC_OPER_ADD)
- {
- r1 = rand() % (math_opts->iopts[MAX_AUGEND] - math_opts->iopts[MIN_AUGEND] + 1) + math_opts->iopts[MIN_AUGEND];
- ret.answer += r1;
- }
- else if (op == MC_OPER_SUB)
- {
- r1 = rand() % (math_opts->iopts[MAX_SUBTRAHEND] - math_opts->iopts[MIN_SUBTRAHEND] + 1) + math_opts->iopts[MIN_SUBTRAHEND];
- ret.answer -= r1;
- }
- else if (op == MC_OPER_MULT)
- {
- r1 = rand() % (math_opts->iopts[MAX_MULTIPLICAND] - math_opts->iopts[MIN_MULTIPLICAND] + 1) + math_opts->iopts[MIN_AUGEND];
- ret.answer *= r1;
- }
- else if (op == MC_OPER_DIV)
- {
- r1 = find_divisor(ret.answer);
- ret.answer /= r1;
- }
- else
- {
- ; //invalid operator
- }
- mcdprintf(" operand is %d\n", r1);
- mcdprintf("Answer: %d\n", ret.answer);
-
- //next append or prepend the new number (might need optimization)
- if (op == MC_OPER_SUB || op == MC_OPER_DIV || //noncommutative, append only
- rand() % 2)
- {
- snprintf(tempstr, max_formula_size, "%s %c %d", //append
- ret.formula_string, operchars[op], r1);
- strncpy(ret.formula_string, tempstr, max_formula_size);
- }
- else //we're prepending
- {
- snprintf(tempstr, max_formula_size, "%d %c %s", //append
- r1, operchars[op], ret.formula_string);
- strncpy(ret.formula_string, tempstr, max_formula_size);
- }
-
- //finally update the answer and score
- snprintf(ret.answer_string, max_answer_size, "%d", ret.answer);
- ret.difficulty += (length - 1) + op;
- }
-
- if (reformat)
- {
- mcdprintf("Reformatting...\n");
- do {
- format = rand() % MC_NUM_FORMATS;
- } while (!MC_GetOpt(FORMAT_ANSWER_LAST + format) &&
- !MC_GetOpt(FORMAT_ADD_ANSWER_LAST + op * 3 + format) );
-
- strncat(ret.formula_string, " = ?", max_formula_size - strlen(ret.formula_string) );
- reformat_arithmetic(&ret, format );
- }
- 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)
- mcdprintf("Randoms chosen: %5f, %5f\n", r1, r2);
- delta = sqrt(-2 * log(r1) ) * cos(2 * PI_VAL * r2); //standard normal dist.
- var = length / 10.0; //variance
- delta = delta * var;
- mcdprintf("Delta of average is %5f\n", delta);
- length += delta;
- if (length < 0)
- length = 1; //just in case...
- }
-
- if (MC_GetOpt(COMPREHENSIVE)) //generate all
- {
- int num_valid_questions; //How many questions the COMPREHENSIVE list specifies
- int cycles_needed; //How many times we need to generate it to get enough
-
- num_valid_questions = calc_num_valid_questions();
- if(num_valid_questions == 0)
- {
- fprintf(stderr, "generate_list() - no valid questions\n");
- return NULL;
- }
-
- cycles_needed = length/num_valid_questions;
-
- if((cycles_needed * num_valid_questions) < length)
- cycles_needed++;
-
- mcdprintf("In generate_list() - COMPREHENSIVE method requested\n");
- mcdprintf("num_valid_questions = %d\t cycles_needed = %d\n",
- num_valid_questions, cycles_needed);
-
- for (i = MC_PT_TYPING; i < MC_NUM_PTYPES; ++i)
- {
- if (!MC_GetOpt(i + TYPING_PRACTICE_ALLOWED))
- continue;
- for (j = 0; j < cycles_needed; j++)
- list = add_all_valid(i, list, &end_of_list);
- }
-
-
- if (MC_GetOpt(RANDOMIZE) )
- {
- mcdprintf("Randomizing list\n");
- randomize_list(&list);
- }
-
- if (length)
- {
- cl = list_length(list);
- // NOTE this should no longer happen - we run the COMPREHENSIVE
- // generation until we have enough questions.
- if (length > cl) //if not enough questions, pad out with randoms
- {
- mcdprintf("Padding out list from %d to %d questions\n", cl, length);
- for (i = cl; i < length; ++i)
- {
- tnode = malloc(sizeof(MC_MathQuestion) );
- if(!tnode)
- {
- fprintf(stderr, "In generate_list() - allocation failed!\n");
- delete_list(list);
- return NULL;
- }
-
- tnode->card = generate_random_flashcard();
- list = insert_node(list, end_of_list, tnode);
- end_of_list = tnode;
- mcdprintf("%d...", list_length(list) );
- }
- }
- else if (length < cl) //if too many questions, chop off tail end of list
- {
- mcdprintf("Cutting list to %d questions\n", length);
- end_of_list = find_node(list, length);
- delete_list(end_of_list->next);
- end_of_list->next = NULL;
- }
- }
- }
-
- /* Here we are just generating random questions, one at a */
- /* time until we have enough */
- else
- {
- mcdprintf("In generate_list() - COMPREHENSIVE method NOT requested\n");
-
- for (i = 0; i < length; ++i)
- {
- tnode = malloc(sizeof(MC_MathQuestion) );
- if(!tnode)
- {
- fprintf(stderr, "In generate_list() - allocation failed!\n");
- delete_list(list);
- return NULL;
- }
-
- tnode->card = generate_random_flashcard();
- list = insert_node(list, end_of_list, tnode);
- end_of_list = tnode;
- }
- }
- return list;
-}
-
-static int compare_card(const MC_FlashCard* a, const MC_FlashCard* b)
-{
- if (strncmp(a->formula_string, b->formula_string, max_formula_size) )
- return 1;
- if (strncmp(a->answer_string, b->answer_string, max_answer_size) )
- 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;
- mcdprintf("Allocating %d + %d bytes for flashcard\n",
- max_formula_size + 1, max_answer_size + 1);
- ret.formula_string = malloc( (max_formula_size + 1) * sizeof(char));
- ret.answer_string = malloc( (max_answer_size + 1) * sizeof(char));
- if (!ret.formula_string || !ret.answer_string)
- {
- free(ret.formula_string);
- free(ret.answer_string);
- printf("Couldn't allocate space for a new flashcard!\n");
- ret = DEFAULT_CARD;
- }
- return ret;
-}
-
-void MC_FreeFlashcard(MC_FlashCard* fc)
-{
- if (!fc)
- return;
-// mcdprintf("Freeing formula_string\n");
- if (fc->formula_string)
- {
- free(fc->formula_string);
- fc->formula_string = NULL;
- }
-// mcdprintf("Freeing answer_string\n");
- if (fc->answer_string)
- {
- free(fc->answer_string);
- fc->answer_string = NULL;
- }
-}
-
-unsigned int MC_MapTextToIndex(const char* text)
-{
- int i;
- for (i = 0; i < NOPTS; ++i)
- {
- if (!strcasecmp(text, MC_OPTION_TEXT[i]) )
- return i;
- }
- mcdprintf("'%s' isn't a math option\n", text);
- return NOT_VALID_OPTION;
-}
-
-
-//TODO more intuitive function names for access by index vs. by text
-void MC_SetOpt(unsigned int index, int val)
-{
- if (index >= NOPTS)
- {
- mcdprintf("Invalid math option index: %d\n", index);
- return;
- }
-
- /* Do some sanity checks before we throw val into the struct: */
- switch(index)
- {
- /* All the booleans must be 0 or 1: */
- case PLAY_THROUGH_LIST:
- case REPEAT_WRONGS:
- case ALLOW_NEGATIVES:
- case FORMAT_ANSWER_LAST:
- case FORMAT_ANSWER_FIRST:
- case FORMAT_ANSWER_MIDDLE:
- case FORMAT_ADD_ANSWER_LAST:
- case FORMAT_ADD_ANSWER_FIRST:
- case FORMAT_ADD_ANSWER_MIDDLE:
- case FORMAT_SUB_ANSWER_LAST:
- case FORMAT_SUB_ANSWER_FIRST:
- case FORMAT_SUB_ANSWER_MIDDLE:
- case FORMAT_MULT_ANSWER_LAST:
- case FORMAT_MULT_ANSWER_FIRST:
- case FORMAT_MULT_ANSWER_MIDDLE:
- case FORMAT_DIV_ANSWER_LAST:
- case FORMAT_DIV_ANSWER_FIRST:
- case FORMAT_DIV_ANSWER_MIDDLE:
- case ADDITION_ALLOWED:
- case SUBTRACTION_ALLOWED:
- case MULTIPLICATION_ALLOWED:
- case DIVISION_ALLOWED:
- case TYPING_PRACTICE_ALLOWED:
- case ARITHMETIC_ALLOWED:
- case COMPARISON_ALLOWED:
- case RANDOMIZE:
- case COMPREHENSIVE:
- case VARY_LIST_LENGTH:
- {
- /* Reset all non-zero values to one: */
- if(val)
- {
- if(val != 1)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to 1\n", MC_OPTION_TEXT[index], val);
- val = 1;
- }
- }
- break;
- }
-
- /* Parameters concerning numbers of questions */
- /* must be greater than or equal to zero: */
- /* TODO some additional checks would make sense */
- case QUESTION_COPIES:
- case COPIES_REPEATED_WRONGS:
- case MAX_QUESTIONS:
- case MAX_FORMULA_NUMS:
- case MIN_FORMULA_NUMS:
- case AVG_LIST_LENGTH:
- {
- /* Reset all negative values to zero: */
- if(val < 0)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to 0\n", MC_OPTION_TEXT[index], val);
- val = 0;
- }
- break;
- }
-
- /* Operand values - make sure they are in displayable range */
- /* i.e. -999 to 999 */
- case MAX_ANSWER:
- case MIN_AUGEND:
- case MAX_AUGEND:
- case MIN_ADDEND:
- case MAX_ADDEND:
- case MIN_MINUEND:
- case MAX_MINUEND:
- case MIN_SUBTRAHEND:
- case MAX_SUBTRAHEND:
- case MIN_MULTIPLIER:
- case MAX_MULTIPLIER:
- case MIN_MULTIPLICAND:
- case MAX_MULTIPLICAND:
- case MIN_DIVISOR:
- case MAX_DIVISOR:
- case MIN_QUOTIENT:
- case MAX_QUOTIENT:
- case MIN_TYPING_NUM:
- case MAX_TYPING_NUM:
- case MIN_COMPARATOR:
- case MAX_COMPARATOR:
- case MIN_COMPARISAND:
- case MAX_COMPARISAND:
- {
- if(val > MC_GLOBAL_MAX)
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to %d\n", MC_OPTION_TEXT[index],
- val, MC_GLOBAL_MAX);
- val = MC_GLOBAL_MAX;
- }
-
- if(val < (0 - MC_GLOBAL_MAX))
- {
- fprintf(stderr, "Warning - parameter %s with invalid value %d, "
- "resetting to %d\n", MC_OPTION_TEXT[index],
- val, (0 - MC_GLOBAL_MAX));
- val = (0 - MC_GLOBAL_MAX);
- }
-
- break;
- }
-
- default:
- fprintf(stderr, "Warning - in MC_SetOpt() - unrecognized index %d\n",
- index);
- }
- /* Should now be safe to put "sanitized" value into struct: */
- math_opts->iopts[index] = val;
-}
-
-void MC_SetOp(const char* param, int val)
-{
- MC_SetOpt(MC_MapTextToIndex(param), val);
-}
-
-int MC_GetOpt(unsigned int index)
-{
- if (index >= NOPTS)
- {
- mcdprintf("Invalid option index: %d\n", index);
- return MC_MATH_OPTS_INVALID;
- }
- if (!math_opts)
- {
- printf("Invalid options list!\n");
- return MC_MATH_OPTS_INVALID;
- }
- return math_opts->iopts[index];
-}
-
-int MC_GetOp(const char* param)
-{
- return MC_GetOpt(MC_MapTextToIndex(param) );
-}
-
-int MC_VerifyOptionListSane(void)
-{
- return strcmp(MC_OPTION_TEXT[NOPTS], "END_OF_OPTS") == 0;
-}
-
-int MC_MaxFormulaSize(void)
-{
- return max_formula_size;
-}
-
-int MC_MaxAnswerSize(void)
-{
- return max_answer_size;
-}
-
-void MC_ResetFlashCard(MC_FlashCard* fc)
-{
- if (!fc || !fc->formula_string || !fc->answer_string)
- return;
- strncpy(fc->formula_string, " ", max_formula_size);
- strncpy(fc->answer_string, " ", max_answer_size);
- fc->answer = 0;
- fc->difficulty = 0;
-}
-
-int MC_FlashCardGood(const MC_FlashCard* fc)
-{
- return fc && fc->formula_string && fc->answer_string;
-}
-
-int find_divisor(int a)
-{
- int div = 1; //the divisor to return
- int realisticpasses = 3; //reasonable time after which a minimum should be met
- int i;
- do
- for (i = 0; i < NPRIMES; ++i) //test each prime
- if (a % smallprimes[i] == 0) //if it is a prime factor,
- if (rand() % (i + 1) == 0) //maybe we'll keep it
- if (div * smallprimes[i] <= MC_GetOpt(MAX_DIVISOR) ) //if we can,
- div *= smallprimes[i]; //update our real divisor
- //keep going if the divisor is too small
- while (div < MC_GetOpt(MIN_DIVISOR) && --realisticpasses);
-
- return div;
-}
-
-
-//Computes (approximately) the number of questions that will be returned
-//by add_all_valid() as specified by the current options. This does not
-//take into account screening out of invalid questions, such
-//as divide-by-zero and questions like "0 x ? = 0".
-static int calc_num_valid_questions(void)
-{
- int total_questions = 0;
- int k = 0;
- //First add the number of typing questions
- if (MC_GetOpt(TYPING_PRACTICE_ALLOWED))
- total_questions += (MC_GetOpt(MAX_TYPING_NUM) - MC_GetOpt(MIN_TYPING_NUM));
-
- //Now add how many questions we will have for each operation:
- for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
- {
- int num_this_oper = 0;
- int formats_this_oper = 0;
-
- if (!MC_GetOpt(k + ADDITION_ALLOWED) )
- continue;
-
- //calculate number of ordered pairs of first and second operands:
- //note the "+ 1" is due to the ranges being inclusive
- num_this_oper = (MC_GetOpt(MAX_AUGEND + 4 * k) - MC_GetOpt(MIN_AUGEND + 4 * k) + 1)
- *
- (MC_GetOpt(MAX_ADDEND + 4 * k) - MC_GetOpt(MIN_ADDEND + 4 * k) + 1);
- //check what formats are allowed
- if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
- formats_this_oper++;
- if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3))
- formats_this_oper++;
- if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
- formats_this_oper++;
- //Get total of e.g. addition questions:
- num_this_oper *= formats_this_oper;
- //add to overall total:
- total_questions += num_this_oper;
- }
-
- //TODO will also need to count up the COMPARISON questions once
- //they are implemented
- {
- }
-
- mcdprintf("calc_num_valid_questions():\t%d\n", total_questions);
- return total_questions;
-}
-
-
-//NOTE end_of_list** needs to be doubly indirect because otherwise the end does not
-//get updated in the calling code
-//NOTE the difficulty is set as add = 1, sub = 2, mult = 3, div = 4, plus a 2 point
-//bonus if the format is a "missing number".
-MC_MathQuestion* add_all_valid(MC_ProblemType pt, MC_MathQuestion* list, MC_MathQuestion** end_of_list)
-{
- int i, j;
- int ans = 0, tmp;
- MC_Operation k;
- MC_MathQuestion* tnode;
-
- mcdprintf("Entering add_all_valid(%d)\n", pt);
- mcdprintf("List already has %d questions\n", list_length(list));
-
- //make sure this problem type is actually allowed
- if (!MC_GetOpt(pt + TYPING_PRACTICE_ALLOWED) )
- return list;
-
- //add all typing questions in range
- if (pt == MC_PT_TYPING)
- {
- mcdprintf("Adding typing...\n");
- for (i = MC_GetOpt(MIN_TYPING_NUM); i <= MC_GetOpt(MAX_TYPING_NUM); ++i)
- {
- mcdprintf("(%d)\n", i);
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.formula_string, max_formula_size, "%d", i);
- snprintf(tnode->card.answer_string, max_formula_size, "%d", i);
- tnode->card.difficulty = 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- }
-
- //add all allowed arithmetic questions
- else if (MC_PT_ARITHMETIC)
- {
- mcdprintf("Adding arithmetic...\n");
-
- // The k loop iterates through the four arithmetic operations:
- // k = 0 means addition
- // k = 1 means subtraction
- // k = 2 means multiplication
- // k = 3 means division
- for (k = MC_OPER_ADD; k < MC_NUM_OPERS; ++k)
- {
- if (!MC_GetOpt(k + ADDITION_ALLOWED) )
- continue;
- mcdprintf("\n*%d*\n", k);
-
- // The i loop iterates through the first value in the question:
- for (i = MC_GetOpt(MIN_AUGEND + 4 * k); i <= MC_GetOpt(MAX_AUGEND + 4 * k); ++i)
- {
- mcdprintf("\n%d:\n", i);
-
- // The j loop iterates through the second value in the question:
- for (j = MC_GetOpt(MIN_ADDEND + 4 * k); j <= MC_GetOpt(MAX_ADDEND + 4 * k); ++j)
- {
- // Generate the third number according to the operation.
- // Although it is called "ans", it will not be the actual
- // answer if it is a "missing number" type problem
- // (e.g. "3 x ? = 12")
- // We also filter out invalid questions here
- switch (k)
- {
- case MC_OPER_ADD:
- {
- ans = i + j;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_SUB:
- {
- ans = i - j;
- // throw out negatives if they aren't allowed:
- if (ans < 0 && !MC_GetOpt(ALLOW_NEGATIVES))
- continue;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_MULT:
- {
- ans = i * j;
- // throw anything over MAX_ANSWER
- if (ans > MC_GetOpt(MAX_ANSWER))
- continue;
- break;
- }
- case MC_OPER_DIV:
- {
- // throw anything over MAX_ANSWER
- if (i * j > MC_GetOpt(MAX_ANSWER))
- continue;
-
- tmp = i;
- i *= j;
- ans = j;
- j = tmp;
- break;
- }
- default:
- fprintf(stderr, "Unrecognized operation type: %d\n", k);
- continue;
- }
-
- mcdprintf("Generating: %d %c %d = %d\n", i, operchars[k], j, ans);
-
- //add each format, provided it's allowed in general and for this op
-
- // Questions like "a + b = ?"
- if (MC_GetOpt(FORMAT_ANSWER_LAST) && MC_GetOpt(FORMAT_ADD_ANSWER_LAST + k * 3))
- {
- // Avoid division by zero:
- if (k == MC_OPER_DIV && j == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, max_formula_size, "%d", ans);
- snprintf(tnode->card.formula_string, max_formula_size,
- "%d %c %d = ?", i, operchars[k], j);
- tnode->card.difficulty = k + 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
-
-
- // Questions like "? + b = c"
- if (MC_GetOpt(FORMAT_ANSWER_FIRST) && MC_GetOpt(FORMAT_ADD_ANSWER_FIRST + k * 3) )
- {
- // Avoid questions with indeterminate answer:
- // e.g. "? x 0 = 0"
- if (k == MC_OPER_MULT && j == 0)
- {
- continue;
- }
- // Avoid division by zero:
- if (k == MC_OPER_DIV && j == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, max_formula_size, "%d", i);
- snprintf(tnode->card.formula_string, max_formula_size,
- "? %c %d = %d", operchars[k], j, ans);
- tnode->card.difficulty = k + 3;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
-
-
- // Questions like "a + ? = c"
- if (MC_GetOpt(FORMAT_ANSWER_MIDDLE) && MC_GetOpt(FORMAT_ADD_ANSWER_MIDDLE + k * 3))
- {
- // Avoid questions with indeterminate answer:
- // e.g. "0 x ? = 0"
- if (k == MC_OPER_MULT && i == 0)
- continue;
-
- // e.g. "0 / ? = 0"
- if (k == MC_OPER_DIV && i == 0)
- {
- // need to restore i and j to original values so loop works:
- j = ans;
- i = tmp;
- continue;
- }
-
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.answer_string, max_formula_size, "%d", j);
- snprintf(tnode->card.formula_string, max_formula_size,
- "%d %c ? = %d", i, operchars[k], ans);
- tnode->card.difficulty = k + 3;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- //If we divided, reset i and j so loop works correctly
- if (k == MC_OPER_DIV)
- {
- j = ans;
- i = tmp;
- mcdprintf("resetting to %d %d\n", j, i);
- }
- }
- }
- }
- }
- //add all comparison questions (TODO implement them!)
- else if (pt == MC_PT_COMPARISON)
- {
- for (i = MC_GetOpt(MIN_COMPARATOR); i < MC_GetOpt(MAX_COMPARATOR); ++i)
- {
- for (j = MC_GetOpt(MIN_COMPARISAND); j < MC_GetOpt(MAX_COMPARISAND); ++j)
- {
- tnode = allocate_node();
- if(!tnode)
- {
- fprintf(stderr, "In add_all_valid() - allocate_node() failed!\n");
- delete_list(list);
- return NULL;
- }
-
- snprintf(tnode->card.formula_string, max_formula_size, "%d ? %d", i,j);
- snprintf(tnode->card.answer_string, max_formula_size,
- i < j ? "<" :
- i > j ? ">" :
- "=");
- tnode->card.difficulty = 1;
- list = insert_node(list, *end_of_list, tnode);
- *end_of_list = tnode;
- }
- }
- }
- mcdprintf("Exiting add_all_valid()\n");
- mcdprintf("List now has %d questions\n\n", list_length(list));
-
- return list;
-}
-
-MC_MathQuestion* find_node(MC_MathQuestion* list, int num)
-{
- while (--num > 0 && list)
- list = list->next;
- return list;
-}
-
-void reformat_arithmetic(MC_FlashCard* card, MC_Format f)
-{
- int i, j;
- char* beg = 0;
- char* end = 0;
- char nans[max_answer_size];
- char nformula[max_formula_size + max_answer_size]; //gets a bit larger than usual in the meantime
-
- {
- //snprintf(nans, max_answer_size, "%s", card->answer_string);
-
- //insert old answer where question mark was
- for (i = 0, j = 0; card->formula_string[j] != '?'; ++i, ++j)
- nformula[i] = card->formula_string[j];
- i += snprintf(nformula + i, max_answer_size - 1, "%s", card->answer_string);
- snprintf(nformula + i, max_formula_size - 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, max_formula_size, "%s?%s", nformula, end);
- snprintf(card->answer_string, max_answer_size, "%s", nans);
- card->answer = atoi(card->answer_string);
- }
-}
Copied: branches/commonification/tuxmath/trunk/src/mathcards.c (from rev 1657, tuxmath/trunk/src/mathcards.c)
===================================================================
--- branches/commonification/tuxmath/trunk/src/mathcards.c (rev 0)
+++ branches/commonification/tuxmath/trunk/src/mathcards.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,2554 @@
+/*
+* 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
+ 100, //AVG_LIST_LENGTH
+ 0 //VARY_LIST_LENGTH FIXME what is the purpose of this?
+};
+
+
+
+/* "Globals" for mathcards.c: */
+#define PI_VAL 3.1415927
+#define NPRIMES 9
+const int smallprimes[NPRIMES] = {2, 3, 5 ,7, 11, 13, 17, 19, 23};
+const char operchars[4] = "+-*/";
+
+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(stderr, "%s\n", ptr->card.formula_string);
+ ptr = ptr->next;
+ }
+}
+
+
+void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length)
+{
+ if (!vect)
+ {
+ fprintf(fp, "\nprint_vect_list(): list empty or pointer invalid\n");
+ return;
+ }
+
+ int i = 0;
+ 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, "%d\n",generate_random_flashcard_id);
+ do
+ pt = rand() % MC_NUM_PTYPES;
+ while ( (pt == MC_PT_TYPING && !MC_GetOpt(TYPING_PRACTICE_ALLOWED) ) ||
+ (pt == MC_PT_ARITHMETIC && !MC_GetOpt(ADDITION_ALLOWED) &&
+ !MC_GetOpt(SUBTRACTION_ALLOWED) &&
+ !MC_GetOpt(MULTIPLICATION_ALLOWED) &&
+ !MC_GetOpt(DIVISION_ALLOWED) ) ||
+ (pt == MC_PT_COMPARISON && !MC_GetOpt(COMPARISON_ALLOWED) )
+ );
+
+ if (pt == MC_PT_TYPING) //typing practice
+ {
+ 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) );
+ 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;
+
+ //FIXME - remind me, why are we doing this??
+ //randomize list length by a "bell curve" centered on average
+ if (length && MC_GetOpt(VARY_LIST_LENGTH) )
+ {
+ r1 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
+ r2 = (double)rand() / RAND_MAX / 2 + 0.5; //interval (0, 1)
+ 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;
+}
+
+
+//TODO more intuitive function names for access by index vs. by text
+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;
+}
+
+void MC_SetOp(const char* param, int val)
+{
+ MC_SetOpt(MC_MapTextToIndex(param), 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_GetOp(const char* param)
+{
+ return MC_GetOpt(MC_MapTextToIndex(param) );
+}
+
+int MC_VerifyOptionListSane(void)
+{
+ return strcmp(MC_OPTION_TEXT[NOPTS], "END_OF_OPTS") == 0;
+}
+
+int MC_MaxFormulaSize(void)
+{
+ return MC_FORMULA_LEN;
+}
+
+int MC_MaxAnswerSize(void)
+{
+ return MC_ANSWER_LEN;
+}
+
+void MC_ResetFlashCard(MC_FlashCard* fc)
+{
+ if (!fc || !fc->formula_string || !fc->answer_string)
+ return;
+ 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]; //gets a bit larger than usual in the meantime
+
+ {
+ //snprintf(nans, max_answer_size, "%s", card->answer_string);
+
+ //insert old answer where question mark was
+ for (i = 0, j = 0; card->formula_string[j] != '?'; ++i, ++j)
+ nformula[i] = card->formula_string[j];
+ i += snprintf(nformula + i, MC_ANSWER_LEN - 1, "%s", card->answer_string);
+ snprintf(nformula + i, MC_FORMULA_LEN - i, "%s", card->formula_string + j + 1);
+
+ //replace the new answer with a question mark
+ if (f == MC_FORMAT_ANS_LAST)
+ beg = strrchr(nformula, ' ') + 1;
+ if (f == MC_FORMAT_ANS_FIRST)
+ beg = nformula;
+ if (f == MC_FORMAT_ANS_MIDDLE)
+ beg = strchr(nformula, ' ') + 3;
+ end = strchr(beg + 1, ' ');
+ if (!end)
+ end = "";
+ //we now have beg = first digit of number to replace, end = the char after
+ sscanf(beg, "%s", nans);
+ *beg = 0; //sequester the first half of the string
+ snprintf(card->formula_string, MC_FORMULA_LEN, "%s?%s", nformula, end);
+ snprintf(card->answer_string, MC_ANSWER_LEN, "%s", nans);
+ card->answer = atoi(card->answer_string);
+ }
+}
+
+static int calc_score(int difficulty, float t)
+{
+ if (t < 0 || difficulty < 1)
+ return 0;
+ return (difficulty * SCORE_COEFFICIENT)/t;
+}
\ No newline at end of file
Deleted: branches/commonification/tuxmath/trunk/src/mathcards.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/mathcards.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/mathcards.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,296 +0,0 @@
-/*
-
- mathcards.h
-
- Description: contains headers for a flashcard-type math game.
- This is a sort of interface-independent backend that could be used with a different
- user interface. Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
- (aka tuxmath). If tuxmath were a C++ program, this would be a C++ class.
-
- Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2006
-
- Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
-
-*/
-#ifndef MATHCARDS_H
-#define MATHCARDS_H
-
-//#define MC_DEBUG
-//#ifdef MC_DEBUG
-#define mcdprintf(...) DEBUGMSG(debug_mathcards,__VA_ARGS__)
-//#else
-//#define mcdprintf(...) 0
-//#endif
-
-#define MC_USE_NEWARC
-
-/* different classes of problems TuxMath will ask */
-typedef enum _MC_ProblemType {
- MC_PT_TYPING,
- MC_PT_ARITHMETIC,
- MC_PT_COMPARISON,
- MC_NUM_PTYPES
-} MC_ProblemType;
-
-/* type of math operation used in an arithmetic question */
-typedef enum _MC_Operation {
- MC_OPER_ADD,
- MC_OPER_SUB,
- MC_OPER_MULT,
- MC_OPER_DIV,
- MC_NUM_OPERS
-} MC_Operation;
-
-/* math question formats: */
-typedef enum _MC_Format {
- MC_FORMAT_ANS_LAST, /* a + b = ? */
- MC_FORMAT_ANS_FIRST, /* ? + b = c */
- MC_FORMAT_ANS_MIDDLE, /* a + ? = c */
- MC_NUM_FORMATS
-} MC_Format;
-
-
-/*
-Indices for the various integer options. These are NOT the actual values!
-Actual values are accessed as such: options.iopts[PLAY_THROUGH_LIST] = val;
-Creating additional [integral] options is now centralized--it should only
-be necessary to add to this list, the list of text, and the list of
-defaults. (Besides actually using the new options!)
-*/
-enum {
- NOT_VALID_OPTION = -1 ,
- PLAY_THROUGH_LIST = 0 , /* play until all questions answered correctly */
- QUESTION_COPIES , /* # times each question is put in list */
- REPEAT_WRONGS , /* reuse incorrectly answered questions or not */
- COPIES_REPEATED_WRONGS , /* how many copies of an incorrectly answered question to re-insert*/
- ALLOW_NEGATIVES ,
- MAX_ANSWER ,
- MAX_QUESTIONS ,
- MAX_FORMULA_NUMS ,
- MIN_FORMULA_NUMS ,
-
- //NOTE: Do _not_ rearrange the FORMAT values because the functions
- //rely on index arithmetic to iterate through these, and will be
- //broken if the relative position changes!
- FORMAT_ANSWER_LAST , /* question format is: a + b = ? */
- FORMAT_ANSWER_FIRST , /* question format is: ? + b = c */
- FORMAT_ANSWER_MIDDLE , /* question format is: a + ? = c */
- FORMAT_ADD_ANSWER_LAST , /* a + b = ? */
- FORMAT_ADD_ANSWER_FIRST , /* ? + b = c */
- FORMAT_ADD_ANSWER_MIDDLE , /* a + ? = c */
- FORMAT_SUB_ANSWER_LAST , /* a - b = ? */
- FORMAT_SUB_ANSWER_FIRST , /* ? - b = c */
- FORMAT_SUB_ANSWER_MIDDLE , /* a - ? = c */
- FORMAT_MULT_ANSWER_LAST , /* a * b = ? */
- FORMAT_MULT_ANSWER_FIRST , /* ? * b = c */
- FORMAT_MULT_ANSWER_MIDDLE , /* a * ? = c */
- FORMAT_DIV_ANSWER_LAST , /* a / b = ? */
- FORMAT_DIV_ANSWER_FIRST , /* ? / b = c */
- FORMAT_DIV_ANSWER_MIDDLE , /* a / ? = c */
-
- ADDITION_ALLOWED ,
- SUBTRACTION_ALLOWED ,
- MULTIPLICATION_ALLOWED ,
- DIVISION_ALLOWED ,
- TYPING_PRACTICE_ALLOWED ,
- ARITHMETIC_ALLOWED ,
- COMPARISON_ALLOWED ,
-
- MIN_AUGEND , /* augend + addend = sum */
- MAX_AUGEND ,
- MIN_ADDEND ,
- MAX_ADDEND ,
-
- MIN_MINUEND , /* minuend - subtrahend = difference */
- MAX_MINUEND ,
- MIN_SUBTRAHEND ,
- MAX_SUBTRAHEND ,
-
- MIN_MULTIPLIER , /* multiplier * multiplicand = product */
- MAX_MULTIPLIER ,
- MIN_MULTIPLICAND ,
- MAX_MULTIPLICAND ,
-
- MIN_DIVISOR , /* dividend/divisor = quotient */
- MAX_DIVISOR , /* note - generate_list() will prevent */
- MIN_QUOTIENT , /* questions with division by zero. */
- MAX_QUOTIENT ,
-
- MIN_TYPING_NUM , /* range for "typing tutor" mode, for */
- MAX_TYPING_NUM , /* kids just learning to use keyboard. */
-
- MIN_COMPARATOR , /* left comparison operand */
- MAX_COMPARATOR ,
- MIN_COMPARISAND , /* right comparison operannd */
- MAX_COMPARISAND ,
-
- RANDOMIZE , /* whether to shuffle cards */
-
- COMPREHENSIVE , /* whether to generate all questions 'in order' */
- AVG_LIST_LENGTH , /* the average number of questions in a list */
- VARY_LIST_LENGTH , /* whether to randomly adjust list length */
-
- NOPTS
-};
-
-extern const char* const MC_OPTION_TEXT[];
-extern const int MC_DEFAULTS[];
-extern const char operchars[MC_NUM_OPERS];
-
-/* default values for math_options */
-#define MC_MAX_DIGITS 3
-#define MC_GLOBAL_MAX 999 /* This is the largest absolute value that */
- /* can be entered for math question values.*/
-#define MC_MATH_OPTS_INVALID -9999 /* Return value for accessor functions */
- /* if math_opts not valid */
-//#define DEFAULT_FRACTION_TO_KEEP 1
-
-
-typedef struct _MC_Options
-{
- int iopts[NOPTS];
-} MC_Options;
-
-#ifndef MC_USE_NEWARC
-/* struct for individual "flashcard" */
-typedef struct MC_FlashCard {
- int num1;
- int num2;
- int num3;
- int operation;
- int format;
- char formula_string[MC_FORMULA_LEN];
- char answer_string[MC_ANSWER_LEN];
-} MC_FlashCard;
-#else
-/* experimental struct for a more generalized flashcard */
-typedef struct _MC_FlashCard {
- char* formula_string;
- char* answer_string;
- int answer;
- int difficulty;
-} MC_FlashCard;
-#endif
-
-/* struct for node in math "flashcard" list */
-typedef struct MC_MathQuestion {
- MC_FlashCard card;
- struct MC_MathQuestion* next;
- struct MC_MathQuestion* previous;
- int randomizer;
-} MC_MathQuestion;
-
-
-/* "public" function prototypes: these functions are how */
-/* a user interface communicates with MathCards: */
-/* TODO provide comments thoroughly explaining these functions */
-
-/* MC_Initialize() sets up the struct containing all of */
-/* settings regarding math questions. It should be */
-/* called before any other function. Many of the other */
-/* functions will not work properly if MC_Initialize() */
-/* has not been called. It only needs to be called once, */
-/* i.e when the program is starting, not at the beginning*/
-/* of each math game for the player. Returns 1 if */
-/* successful, 0 otherwise. */
-int MC_Initialize(void);
-
-/* MC_StartGame() generates the list of math questions */
-/* based on existing settings. It should be called at */
-/* the beginning of each math game for the player. */
-/* Returns 1 if resultant list contains 1 or more */
-/* questions, 0 if list empty or not generated */
-/* successfully. */
-int MC_StartGame(void);
-
-/* MC_StartGameUsingWrongs() is like MC_StartGame(), */
-/* but uses the incorrectly answered questions from the */
-/* previous game for the question list as a review form */
-/* of learning. If there were no wrong answers (or no */
-/* previous game), it behaves just like MC_StartGame(). */
-/* FIXME wonder if it should generate a message if the */
-/* list is created from settings because there is no */
-/* valid wrong question list? */
-int MC_StartGameUsingWrongs(void);
-
-/* MC_NextQuestion() takes a pointer to an allocated */
-/* MC_MathQuestion struct and fills in the fields for */
-/* use by the user interface program. It basically is */
-/* like taking the next flashcard from the pile. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-/* or if argument pointer is invalid */
-int MC_NextQuestion(MC_FlashCard* q);
-
-/* MC_AnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has been answered */
-/* correctly. Returns 1 if no errors. */
-int MC_AnsweredCorrectly(MC_FlashCard* q);
-
-/* MC_NotAnsweredCorrectly() is how the user interface */
-/* tells MathCards that the question has not been */
-/* answered correctly. Returns 1 if no errors. */
-int MC_NotAnsweredCorrectly(MC_FlashCard* q);
-
-/* Like MC_NextQuestion(), but takes "flashcard" from */
-/* pile of incorrectly answered questions. */
-/* Returns 1 if question found, 0 if list empty/invalid */
-int MC_NextWrongQuest(MC_FlashCard* q);
-
-/* Returns 1 if all have been answered correctly, */
-/* 0 otherwise. */
-int MC_MissionAccomplished(void);
-
-/* Returns number of questions left (either in list */
-/* or "in play") */
-int MC_TotalQuestionsLeft(void);
-
-/* Returns questions left in list, NOT */
-/* including questions currently "in play". */
-int MC_ListQuestionsLeft(void);
-
-/* To keep track of how long students take to answer the */
-/* questions, one can report the time needed to answer */
-/* an individual question: */
-int MC_AddTimeToList(float t);
-/* Note that initialization of the list is handled by */
-/* MC_StartGame. */
-
-/* Tells MathCards to clean up - should be called when */
-/* user interface program exits. */
-void MC_EndGame(void);
-
-/* Prints contents of math_opts struct in human-readable */
-/* form to given file. "verbose" tells the function to */
-/* write a lot of descriptive "help"-type info for each */
-/* option (intended to make config files self-documenting).*/
-void MC_PrintMathOptions(FILE* fp, int verbose);
-
-/* Additional functions used to generate game summary files: */
-int MC_PrintQuestionList(FILE* fp);
-int MC_PrintWrongList(FILE* fp);
-int MC_StartingListLength(void);
-int MC_WrongListLength(void);
-int MC_NumAnsweredCorrectly(void);
-int MC_NumNotAnsweredCorrectly(void);
-float MC_MedianTimePerQuestion(void);
-
-/********************************************
-Public functions for new mathcards architecture
-*********************************************/
-/* Return the array index of the given text, e.g. randomize->47 */
-unsigned int MC_MapTextToIndex(const char* text);
-void MC_SetOpt(unsigned int index, int val); //access directly,for internal use
-int MC_GetOpt(unsigned int index);
-void MC_SetOp(const char* param, int val); //access by text, for config reading
-int MC_GetOp(const char* param);
-int MC_VerifyOptionListSane(void);
-int MC_MaxFormulaSize(void); //amount of memory needed to safely hold strings
-int MC_MaxAnswerSize(void);
-MC_FlashCard MC_AllocateFlashcard();
-void MC_FreeFlashcard(MC_FlashCard* fc);
-void MC_ResetFlashCard(MC_FlashCard* fc); //empty flashcard of strings & values
-int MC_FlashCardGood(const MC_FlashCard* fc); //verifies a flashcard is valid
-/* Reorganize formula_string and answer_string to render the same equation
- in a different format */
-void reformat_arithmetic(MC_FlashCard* card, MC_Format f);
-#endif
Copied: branches/commonification/tuxmath/trunk/src/mathcards.h (from rev 1657, tuxmath/trunk/src/mathcards.h)
===================================================================
--- branches/commonification/tuxmath/trunk/src/mathcards.h (rev 0)
+++ branches/commonification/tuxmath/trunk/src/mathcards.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,282 @@
+/*
+
+ mathcards.h
+
+ Description: contains headers for a flashcard-type math game.
+ This is a sort of interface-independent backend that could be used with a different
+ user interface. Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
+ (aka tuxmath). If tuxmath were a C++ program, this would be a C++ class.
+
+ Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2006
+
+ Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
+
+*/
+
+
+#ifndef MATHCARDS_H
+#define MATHCARDS_H
+
+#include "transtruct.h"
+
+
+/* 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); //access directly,for internal use
+int MC_GetOpt(unsigned int index);
+void MC_SetOp(const char* param, int val); //access by text, for config reading
+int MC_GetOp(const char* param);
+int MC_VerifyOptionListSane(void);
+int MC_MaxFormulaSize(void); //amount of memory needed to safely hold strings
+int MC_MaxAnswerSize(void);
+MC_FlashCard MC_AllocateFlashcard();
+void MC_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
Modified: branches/commonification/tuxmath/trunk/src/menu.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/menu.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/menu.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -22,6 +22,11 @@
#include "SDL_extras.h"
#include "titlescreen.h"
+#ifdef HAVE_LIBSDL_NET
+#include "network.h"
+#include "server.h"
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -132,6 +137,16 @@
MenuNode* load_menu_from_file(FILE* xml_file, MenuNode* parent);
void free_menu(MenuNode* menu);
+int handle_activity(int act, int param);
+int run_academy(void);
+int run_arcade(int choice);
+int run_custom_game(void);
+void run_multiplayer(int mode, int difficulty);
+int run_factoroids(int choice);
+int run_lan_join(void);
+int run_lan_host(void);
+
+int run_menu(MenuNode* menu, bool return_choice);
SDL_Surface** render_buttons(MenuNode* menu, bool selected);
char* find_longest_text(MenuNode* menu, int* length);
int find_longest_menu_page(MenuNode* menu);
@@ -352,12 +367,101 @@
if(menus[index])
{
- free_menu(menus[index]);
- menus[index] = NULL;
+ case RUN_CAMPAIGN:
+ start_campaign();
+ break;
+
+ case RUN_ACADEMY:
+ if(run_academy() == QUIT)
+ return QUIT;
+ break;
+
+ case RUN_ARCADE:
+ 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;
+
+ case RUN_HALL_OF_FAME:
+ DisplayHighScores(CADET_HIGH_SCORE);
+ break;
+
+ case RUN_SCORE_SWEEP:
+ run_multiplayer(0, param);
+ break;
+
+ case RUN_ELIMINATION:
+ run_multiplayer(1, param);
+ break;
+
+ case RUN_HELP:
+ Opts_SetHelpMode(1);
+ Opts_SetDemoMode(0);
+ if (Opts_GetGlobalOpt(MENU_MUSIC)) //Turn menu music off for game
+ {audioMusicUnload();}
+ game();
+ if (Opts_GetGlobalOpt(MENU_MUSIC)) //Turn menu music back on
+ audioMusicLoad( "tuxi.ogg", -1 );
+ Opts_SetHelpMode(0);
+ break;
+
+ case RUN_FACTORS:
+ run_factoroids(0);
+ break;
+
+ case RUN_FRACTIONS:
+ run_factoroids(1);
+ break;
+
+ case RUN_DEMO:
+ if(read_named_config_file("demo"))
+ {
+ audioMusicUnload();
+ game();
+ if (Opts_GetGlobalOpt(MENU_MUSIC))
+ audioMusicLoad( "tuxi.ogg", -1 );
+ }
+ else
+ fprintf(stderr, "\nCould not find demo config file\n");
+ break;
+
+ case RUN_INFO:
+ ShowMessage(DEFAULT_MENU_FONT_SIZE,
+ _("TuxMath is free and open-source!"),
+ _("You can help make it better."),
+ _("Suggestions, artwork, and code are all welcome!"),
+ _("Discuss TuxMath at tuxmath-devel at lists.sourceforge.net"));
+ break;
+
+ case RUN_CREDITS:
+ credits();
+ break;
+
+ case RUN_QUIT:
+ return QUIT;
}
- menu_file = fopen(file_name, "r");
- if(menu_file == NULL)
+ DEBUGMSG(debug_menu, "Leaving handle_activity\n");
+
+ return 0;
+}
+
+int run_academy(void)
+{
+ int chosen_lesson = -1;
+
+ chosen_lesson = run_menu(menus[MENU_LESSONS], true);
+ while (chosen_lesson >= 0)
{
DEBUGMSG(debug_menu, "LoadMenu(): Could not load %s !\n", file_name);
}
@@ -371,7 +475,12 @@
/* free all loaded menu trees */
void UnloadMenus(void)
{
- int i;
+ const char *s1, *s2, *s3, *s4;
+ s1 = _("Edit 'options' file in your home directory");
+ s2 = _("to create customized game!");
+ s3 = _("Press a key or click your mouse to start game.");
+ s4 = _("See README.txt for more information");
+ ShowMessage(DEFAULT_MENU_FONT_SIZE, s1, s2, s3, s4);
DEBUGMSG(debug_menu, "entering UnloadMenus()\n");
@@ -387,11 +496,46 @@
prev_arrow = NULL;
}
- if(next_arrow)
- {
- SDL_FreeSurface(next_arrow);
- next_arrow = NULL;
+ mp_set_parameter(PLAYERS, nplayers);
+ mp_set_parameter(MODE, mode);
+ mp_set_parameter(DIFFICULTY, difficulty);
+ mp_run_multiplayer();
+}
+
+int run_factoroids(int choice)
+{
+ const int factoroids_high_score_tables[2] =
+ {FACTORS_HIGH_SCORE, FRACTIONS_HIGH_SCORE};
+ int hs_table;
+
+ audioMusicUnload();
+ if(choice == 0)
+ factors();
+ else
+ fractions();
+
+ if (Opts_GetGlobalOpt(MENU_MUSIC))
+ audioMusicLoad( "tuxi.ogg", -1 );
+
+ hs_table = factoroids_high_score_tables[choice];
+ if (check_score_place(hs_table, Opts_LastScore()) < HIGH_SCORES_SAVED){
+ char player_name[HIGH_SCORE_NAME_LENGTH * 3];
+ /* Get name from player: */
+ HighScoreNameEntry(&player_name[0]);
+ insert_score(player_name, hs_table, Opts_LastScore());
+ /* Show the high scores. Note the user will see his/her */
+ /* achievement even if (in the meantime) another player */
+ /* has in fact already bumped this score off the table. */
+ DisplayHighScores(hs_table);
+ /* save to disk: */
+ /* See "On File Locking" in fileops.c */
+ append_high_score(hs_table,Opts_LastScore(),&player_name[0]);
+ DEBUGCODE(debug_titlescreen)
+ print_high_scores(stderr);
}
+ else {
+ fprintf(stderr, "\nCould not find config file\n");
+ }
for(i = 0; i < N_OF_MENUS; i++)
if(menus[i] != NULL)
@@ -404,8 +548,136 @@
}
-/*
- RunMenu - main function to display the menu and run the event loop
+/* 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
+
+#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()
this function is a modified copy of choose_menu_item()
@@ -845,11 +1117,17 @@
tmp_sprite = menu->submenu[menu->first_entry + loc]->icon;
if(tmp_sprite)
{
- SDL_BlitSurface(menu_item_selected[loc], NULL, GetScreen(), &menu->submenu[menu->first_entry + loc]->icon_rect);
- SDL_BlitSurface(tmp_sprite->frame[tmp_sprite->cur], NULL, GetScreen(), &menu->submenu[menu->first_entry + loc]->icon_rect);
- UpdateRect(GetScreen(), &menu->submenu[menu->first_entry + loc]->icon_rect);
+ DEBUGMSG(debug_menu, "run_menu(): incrementing sprite for entry %d\n", loc);
+
+ SDL_BlitSurface(menu_item_selected[loc], NULL, screen,
+ &menu->submenu[menu->first_entry + loc]->icon_rect);
+ SDL_BlitSurface(tmp_sprite->frame[tmp_sprite->cur], NULL, screen,
+ &menu->submenu[menu->first_entry + loc]->icon_rect);
+ UpdateRect(screen, &menu->submenu[menu->first_entry + loc]->icon_rect);
NextFrame(tmp_sprite);
}
+ else
+ DEBUGMSG(debug_menu, "run_menu(): entry %d has no valid sprite\n", loc);
}
/* handle titlescreen animations */
@@ -914,6 +1192,11 @@
SDL_FreeSurface(tmp_surf);
/* text */
+ DEBUGMSG(debug_menu, "render_buttons(): English string is: %s\n",
+ menu->submenu[menu->first_entry + i]->title);
+ DEBUGMSG(debug_menu, "render_buttons(): NLS string is: %s\n",
+ _(menu->submenu[menu->first_entry + i]->title));
+
tmp_surf = BlackOutline(_(menu->submenu[menu->first_entry + i]->title),
curr_font_size, selected ? &yellow : &white);
SDL_BlitSurface(tmp_surf, NULL, menu_items[i], &menu->submenu[menu->first_entry + i]->text_rect);
Modified: branches/commonification/tuxmath/trunk/src/menu.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/menu.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/menu.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -15,9 +15,15 @@
#ifndef MENU_H
#define MENU_H
-#include "tuxmath.h"
+#include "globals.h"
+#include "loaders.h"
#ifndef HAVE_LIBT4KCOMMON
+#include "SDL.h"
+/* titlescreen & menu frame rate */
+#define MAX_FPS 30
+/* number of "real" frames per one sprite frame */
+#define SPRITE_FRAME_DELAY 6
#define MAX_FPS 30
/* special values used by RunMenu. RUN_MAIN_MENU is a special
Modified: branches/commonification/tuxmath/trunk/src/network.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/network.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/network.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,136 +1,503 @@
+/*
+* C Implementation: network.c
+*
+* Description: Contains all the network realted 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).
+*/
-//** this file would contain all the network related functions**
-
-
-
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
#include <unistd.h>
-#include "SDL.h"
-#include "SDL_net.h"
+#include <fcntl.h>
-//*** ipaddress of the server and the port would be taken by the user @ the time he selects "LAN multiplayer" from the options menu..
+#include "globals.h"
+#include "mathcards.h"
+#include "transtruct.h"
+#include "network.h"
+#include "throttle.h"
+//#include "testclient.h"
-//***also should I fix the port beforehand or ask it from the user...
-int lan_client_connect(char *host,int port) //here "host" is either the hostname or the ipaddress(of the server) in string
+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)
{
-IPaddress ip;
-TCPsocket sock;
-Uint16 portnum;
+ 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;
-portnum=(Uint16) strtol(port,NULL,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
-if(SDL_Init(0)==-1)
+ /* 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)
{
-printf("SDL_Init: %s\n",SDL_GetError());
-exit(1);
+ if(i < 0 || i > MAX_SERVERS)
+ return NULL;
+ if(servers[i].ip.host != 0)
+ return servers[i].name;
+ else
+ return NULL;
}
-// initialize SDL_net
-if(SDLNet_Init()==-1)
+char* LAN_ConnectedServerName(void)
{
-printf("SDLNet_Init: %s\n",SDLNet_GetError());
-exit(2);
+ 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;
-// Resolve the argument into an IPaddress type
-if(SDLNet_ResolveHost(&ip,*host,portnum)==-1)
+ /* 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)
{
-printf("SDLNet_ResolveHost: %s\n",SDLNet_GetError());
-exit(3);
+ if(sd)
+ {
+ SDLNet_TCP_Close(sd);
+ sd = NULL;
+ }
+
+ if(set)
+ {
+ SDLNet_FreeSocketSet(set);
+ set = NULL;
+ }
+ SDLNet_Quit();
}
-//connect to the "host" @ port "portnum"
-sock=SDLNet_TCP_Open(&ip);
-if(!sock)
+
+
+int LAN_SetName(char* name)
{
-printf("SDLNet_TCP_Open: %s\n",SDLNet_GetError());
-exit(4);
+ 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);
}
-return(0);
+
+
+
+/* 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;
}
-/*** server connection function ****/
+/* 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;
-int lan_server_connect(int port)
+ /* 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)
{
-IPaddress ip; //int *remoteip;
-TCPsocket server,client;
-//Uint32 ipaddr;
-Uint16 portnum;
+ 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);
-// initialize SDL
-if(SDL_Init(0)==-1)
+ DEBUGMSG(debug_lan, "In Make_Flashcard, new card is:\n");
+ DEBUGCODE(debug_lan) print_card(*fc);
+
+return 1;
+}
+
+
+int LAN_StartGame(void)
{
-printf("SDL_Init: %s\n",SDL_GetError());
-exit(1);
+ char buffer[NET_BUF_LEN];
+ snprintf(buffer, NET_BUF_LEN, "%s", "START_GAME");
+ return say_to_server(buffer);
}
-// initialize SDL_net
-if(SDLNet_Init()==-1)
+
+int LAN_AnsweredCorrectly(int id, float t)
{
-printf("SDLNet_Init: %s\n",SDLNet_GetError());
-exit(2);
+ char buffer[NET_BUF_LEN];
+ snprintf(buffer, NET_BUF_LEN, "%s\t%d\t%f", "CORRECT_ANSWER", id, t);
+ return say_to_server(buffer);
}
-portnum=(Uint16)strtol(port,NULL,0);
-
-// Resolve the argument into an IPaddress type
-if(SDLNet_ResolveHost(&ip,NULL,portum)==-1)
+int LAN_NotAnsweredCorrectly(int id)
{
-printf("SDLNet_ResolveHost: %s\n",SDLNet_GetError());
-exit(3);
+ char buffer[NET_BUF_LEN];
+ snprintf(buffer, NET_BUF_LEN, "%s\t%d", "WRONG_ANSWER", id);
+ return say_to_server(buffer);
}
-// open the server socket
-server=SDLNet_TCP_Open(&ip);
-if(!server)
+
+/* This tells the server we are quitting the current math game, but */
+/* not disconnecting our socket: */
+int LAN_LeaveGame(void)
{
-printf("SDLNet_TCP_Open: %s\n",SDLNet_GetError());
-exit(4);
+ char buf[NET_BUF_LEN];
+ snprintf(buf, NET_BUF_LEN, "%s", "LEAVE_GAME");
+ return say_to_server(buf);
}
-while(1)
+
+
+/*private to network.c functions*/
+
+int say_to_server(char* statement)
{
-// try to accept a connection
-client=SDLNet_TCP_Accept(server);
-if(!client)
-{ // no connection accepted
-//printf("SDLNet_TCP_Accept: %s\n",SDLNet_GetError());
-SDL_Delay(100); //sleep 1/10th of a second
-continue;
+ 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;
}
-/// get the clients IP and port number
-//remoteip=SDLNet_TCP_GetPeerAddress(client);
-//if(!remoteip)
-//{
-//printf("SDLNet_TCP_GetPeerAddress: %s\n",SDLNet_GetError());
-//continue;
-//}
+//add name to list, checking for duplicates:
+int add_to_server_list(UDPpacket* pkt)
+{
+ int i = 0;
+ int already_in = 0;
+ char* p = NULL;
-/*for testing purpose to check if it is connected to the desired client
-// print out the clients IP and port number
-ipaddr=SDL_SwapBE32(remoteip->host);
-printf("Accepted a connection from %d.%d.%d.%d port %hu\n",
-ipaddr>>24,
-(ipaddr>>16)&0xff,
-(ipaddr>>8)&0xff,
-ipaddr&0xff,
-remoteip->port);
+ 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);
-return(0);
+ 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++;
+ }
+}
+
+
Copied: branches/commonification/tuxmath/trunk/src/network.h (from rev 1657, tuxmath/trunk/src/network.h)
===================================================================
--- branches/commonification/tuxmath/trunk/src/network.h (rev 0)
+++ branches/commonification/tuxmath/trunk/src/network.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,59 @@
+/*
+
+ 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 "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 // NETWORK_H
Modified: branches/commonification/tuxmath/trunk/src/options.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/options.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/options.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -123,7 +123,9 @@
global_options->iopts[FULLSCREEN] = DEFAULT_FULLSCREEN;
global_options->iopts[USE_KEYPAD] = DEFAULT_USE_KEYPAD;
global_options->iopts[USE_IGLOOS] = DEFAULT_USE_IGLOOS;
- strncpy(game_options->current_font_name, DEFAULT_FONT_NAME, sizeof(game_options->current_font_name));
+ strncpy(game_options->current_font_name, DEFAULT_FONT_NAME,
+ sizeof(game_options->current_font_name));
+ game_options->lan_mode = DEFAULT_LAN_MODE;
game_options->use_bkgd = DEFAULT_USE_BKGD;
game_options->help_mode = DEFAULT_HELP_MODE;
game_options->demo_mode = DEFAULT_DEMO_MODE;
@@ -246,6 +248,12 @@
// global_options->iopts[FULLSCREEN] = int_to_bool(val);
//}
+void Opts_SetLanMode(int val)
+{
+ game_options->lan_mode = int_to_bool(val);
+}
+
+
void Opts_SetFontName(char* font_name)
{
if (font_name && font_name[0] != '\0')
@@ -613,6 +621,18 @@
// return global_options->iopts[FULLSCREEN];
//}
+
+int Opts_LanMode(void)
+{
+ if (!game_options)
+ {
+ fprintf(stderr, "\nOpts_LanMode(): game_options not valid!\n");
+ return GAME_OPTS_INVALID;
+ }
+ return game_options->lan_mode;
+}
+
+
const char* Opts_FontName(void)
{
if (!game_options)
Modified: branches/commonification/tuxmath/trunk/src/options.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/options.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/options.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -49,6 +49,7 @@
typedef struct game_option_type {
/* general game options */
char current_font_name[FONT_NAME_LENGTH];
+ int lan_mode;
int use_bkgd;
int help_mode;
int demo_mode;
@@ -63,7 +64,7 @@
int slow_after_wrong;
int starting_comets;
int extra_comets_per_wave;
- int max_comets;
+ int max_comets;
char next_mission[PATH_MAX];
int save_summary;
int use_feedback;
@@ -121,6 +122,7 @@
void Opts_SetGlobalOpt(unsigned int index, int val);
void Opts_SetFontName(char* font_name);
+void Opts_SetLanMode(int val);
void Opts_SetUseBkgd(int val);
void Opts_SetHelpMode(int val);
void Opts_SetDemoMode(int val);
@@ -154,6 +156,7 @@
/* "Get" functions for tuxmath options struct: */
const char* Opts_FontName(void);
+int Opts_LanMode(void);
int Opts_UseBkgd(void);
int Opts_HelpMode(void);
int Opts_DemoMode(void);
Modified: branches/commonification/tuxmath/trunk/src/scandir.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/scandir.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/scandir.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -29,9 +29,9 @@
#include "scandir.h"
/*-----------------------------------------------------------------------
- * Here come alphasort and scandir for BeOS and SunOS
+ * Here come alphasort and scandir for BeOS/Haiku and SunOS
*-----------------------------------------------------------------------*/
-#if defined(__BEOS__) || (defined(__sun) && defined(__SVR4))
+#if defined(__BEOS__) || defined(__HAIKU__) || (defined(__sun) && defined(__SVR4))
#undef DIRSIZ
Modified: branches/commonification/tuxmath/trunk/src/scandir.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/scandir.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/scandir.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -19,7 +19,7 @@
#define dirent direct
#endif
-#if defined(__BEOS__) || (defined(__sun) && defined(__SVR4)) || defined(WIN32)
+#if defined(__BEOS__) || defined(__HAIKU__) || (defined(__sun) && defined(__SVR4)) || defined(WIN32)
extern int alphasort(const void *d1, const void *d2);
extern int scandir(const char *dirname, struct dirent ***namelist, int (*sdfilter)(struct dirent *), int (*dcomp)(const void *, const void *));
#endif
Copied: branches/commonification/tuxmath/trunk/src/server.c (from rev 1657, tuxmath/trunk/src/server.c)
===================================================================
--- branches/commonification/tuxmath/trunk/src/server.c (rev 0)
+++ branches/commonification/tuxmath/trunk/src/server.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,1241 @@
+/*
+* 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
+*/
+
+#include "globals.h"
+#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);
+}
+
+
+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;
+}
+
+/*********************************************************************/
+/* "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';
+ 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);
+ }
+
+
+ // 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;
+}
+
+
Copied: branches/commonification/tuxmath/trunk/src/server.h (from rev 1657, tuxmath/trunk/src/server.h)
===================================================================
--- branches/commonification/tuxmath/trunk/src/server.h (rev 0)
+++ branches/commonification/tuxmath/trunk/src/server.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,72 @@
+/*
+
+ 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 SERVER_H
+#define SERVER_H == 1)
+
+#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
Copied: branches/commonification/tuxmath/trunk/src/servermain.c (from rev 1657, tuxmath/trunk/src/servermain.c)
===================================================================
--- branches/commonification/tuxmath/trunk/src/servermain.c (rev 0)
+++ branches/commonification/tuxmath/trunk/src/servermain.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,23 @@
+/*
+* 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)
+{
+ return RunServer(argc, argv);
+}
Modified: branches/commonification/tuxmath/trunk/src/setup.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/setup.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/setup.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -43,7 +43,6 @@
#include "mathcards.h"
#include "setup.h"
#include "fileops.h"
-#include "fileops_media.h"
#include "loaders.h"
#include "game.h"
#include "menu.h"
@@ -98,6 +97,22 @@
void cleanup_memory(void);
+/*
+HACK these two getters are declared in fileops.h and don't seem to be
+implemented anywhere else, but can only belong here...
+*/
+sprite* GetSprite(int id)
+{
+ if (id >= NUM_SPRITES)
+ return NULL;
+ return sprites[id];
+}
+SDL_Surface* GetImage(int id)
+{
+ if (id >= NUM_IMAGES)
+ return NULL;
+ return images[id];
+}
/* --- Set-up function - now in four easier-to-digest courses! --- */
@@ -273,7 +288,8 @@
strcmp(argv[i], "-c") == 0)
{
printf(
- "\n\"Tux, of Math Command\" version " VERSION ", Copyright (C) 2001 Bill Kendrick\n"
+ "\n\"Tux, of Math Command\" version " VERSION ", Copyright (C) 2001-2009,\n"
+ "Bill Kendrick, David Bruce, Tim Holy, 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"
Copied: branches/commonification/tuxmath/trunk/src/testclient.c (from rev 1657, tuxmath/trunk/src/testclient.c)
===================================================================
--- branches/commonification/tuxmath/trunk/src/testclient.c (rev 0)
+++ branches/commonification/tuxmath/trunk/src/testclient.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,536 @@
+/*
+* 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
+*/
+
+#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;
+}
Copied: branches/commonification/tuxmath/trunk/src/testclient.h (from rev 1657, tuxmath/trunk/src/testclient.h)
===================================================================
--- branches/commonification/tuxmath/trunk/src/testclient.h (rev 0)
+++ branches/commonification/tuxmath/trunk/src/testclient.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,35 @@
+/*
+
+ 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
Copied: branches/commonification/tuxmath/trunk/src/throttle.c (from rev 1657, tuxmath/trunk/src/throttle.c)
===================================================================
--- branches/commonification/tuxmath/trunk/src/throttle.c (rev 0)
+++ branches/commonification/tuxmath/trunk/src/throttle.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,45 @@
+/*
+* 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();
+}
Copied: branches/commonification/tuxmath/trunk/src/throttle.h (from rev 1657, tuxmath/trunk/src/throttle.h)
===================================================================
--- branches/commonification/tuxmath/trunk/src/throttle.h (rev 0)
+++ branches/commonification/tuxmath/trunk/src/throttle.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,24 @@
+/*
+
+ 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
Modified: branches/commonification/tuxmath/trunk/src/titlescreen.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/titlescreen.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/titlescreen.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -33,11 +33,11 @@
#include "mathcards.h"
#include "campaign.h"
#include "fileops.h"
-#include "fileops_media.h"
#include "setup.h"
#include "loaders.h"
#include "SDL_extras.h"
#include "menu.h"
+#include "throttle.h"
//NOTE tuxmath.h takes care of this:
//#ifdef HAVE_LIBT4KCOMMON
@@ -141,6 +141,7 @@
/* This syntax makes my brain start to explode! */
{ return GetScreen()->flags & SDL_FULLSCREEN ? fs_bkg : win_bkg; }
+/* FIXME don't we have to scale these? */
void set_current_bkg(SDL_Surface* new_bkg)
{
if(GetScreen()->flags & SDL_FULLSCREEN)
@@ -263,7 +264,7 @@
/* NOTE: do we need this ? */
DEBUGCODE(debug_titlescreen)
SDL_WM_GrabInput(SDL_GRAB_OFF); /* in case of a freeze, this traps the cursor */
- else
+ else // NOTE- the accompanying "if" is inside the DEBUGCODE macro
SDL_WM_GrabInput(SDL_GRAB_ON); /* User input goes to TuxMath, not window manager */
SDL_ShowCursor(1);
@@ -566,7 +567,8 @@
break;
case RUN_INFO:
- ShowMessage(_("TuxMath is free and open-source!"),
+ ShowMessage(DEFAULT_MENU_FONT_SIZE,
+ _("TuxMath is free and open-source!"),
_("You can help make it better by reporting problems,"),
_("suggesting improvements, or adding code."),
_("Discuss the future at tuxmath-devel at lists.sourceforge.net"));
@@ -689,7 +691,7 @@
s2 = _("to create customized game!");
s3 = _("Press a key or click your mouse to start game.");
s4 = _("See README.txt for more information");
- ShowMessage(s1, s2, s3, s4);
+ ShowMessage(DEFAULT_MENU_FONT_SIZE, s1, s2, s3, s4);
if (read_user_config_file()) {
AudioMusicUnload();
@@ -933,34 +935,36 @@
s3 = _("Discuss the future of TuxMath at");
s4 = N_("tuxmath-devel at lists.sourceforge.net");
- ShowMessage(s1, s2, s3, s4);
+ ShowMessage(DEFAULT_MENU_FONT_SIZE, s1, s2, s3, s4);
}
-/* FIXME add some background shading to improve legibility */
-void ShowMessage(const char* str1, const char* str2, const char* str3, const char* str4)
+void ShowMessage(int font_size, const char* str1, const char* str2,
+ const char* str3, const char* str4)
{
SDL_Surface *s1, *s2, *s3, *s4;
SDL_Rect loc;
int finished = 0;
- Uint32 frame = 0;
- Uint32 start = 0;
+ Uint32 timer = 0;
+ /* To adjust font size: */
+ float scale = screen->w / 640;
+
s1 = s2 = s3 = s4 = NULL;
DEBUGMSG(debug_titlescreen, "ShowMessage() - creating text\n" );
if (str1)
- s1 = BlackOutline(str1, DEFAULT_MENU_FONT_SIZE, &white);
+ s1 = BlackOutline(str1, font_size * scale, &white);
if (str2)
- s2 = BlackOutline(str2, DEFAULT_MENU_FONT_SIZE, &white);
+ s2 = BlackOutline(str2, font_size * scale, &white);
if (str3)
- s3 = BlackOutline(str3, DEFAULT_MENU_FONT_SIZE, &white);
+ s3 = BlackOutline(str3, font_size * scale, &white);
if (str4)
- s4 = BlackOutline(str4, DEFAULT_MENU_FONT_SIZE, &white);
+ s4 = BlackOutline(str4, font_size * scale, &white);
DEBUGMSG(debug_titlescreen, "ShowMessage() - drawing screen\n" );
@@ -969,27 +973,35 @@
if (stop_button)
SDL_BlitSurface(stop_button, NULL, screen, &stop_rect);
+ /* Draw shaded background for better legibility: */
+ loc.x = screen->w * 0.25;
+ loc.y = screen->h * 0.1;
+ loc.w = screen->w * 0.5;
+ loc.h = screen->h * 0.8;
+ DrawButton(&loc, 50, SEL_RGBA);
+
+
/* 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 = 10;
+ loc.x = (screen->w / 2) - (s1->w/2); loc.y = screen->h * 0.2;
SDL_BlitSurface( s1, NULL, screen, &loc);
}
if (s2)
{
- loc.x = (screen->w / 2) - (s2->w/2); loc.y = 60;
+ loc.x = (screen->w / 2) - (s2->w/2); loc.y = screen->h * 0.35;
SDL_BlitSurface( s2, NULL, screen, &loc);
}
if (s3)
{
//loc.x = 320 - (s3->w/2); loc.y = 300;
- loc.x = (screen->w / 2) - (s3->w/2); loc.y = 110;
+ loc.x = (screen->w / 2) - (s3->w/2); loc.y = screen->h * 0.5;
SDL_BlitSurface( s3, NULL, screen, &loc);
}
if (s4)
{
//loc.x = 320 - (s4->w/2); loc.y = 340;
- loc.x = (screen->w / 2) - (s4->w/2); loc.y = 200;
+ loc.x = (screen->w / 2) - (s4->w/2); loc.y = screen->h * 0.65;
SDL_BlitSurface( s4, NULL, screen, &loc);
}
@@ -998,8 +1010,6 @@
while (!finished)
{
- start = SDL_GetTicks();
-
while (SDL_PollEvent(&event))
{
switch (event.type)
@@ -1030,11 +1040,7 @@
HandleTitleScreenAnimations();
/* Wait so we keep frame rate constant: */
- while ((SDL_GetTicks() - start) < 33)
- {
- SDL_Delay(20);
- }
- frame++;
+ Throttle(20, &timer);
} // End of while (!finished) loop
SDL_FreeSurface(s1);
Modified: branches/commonification/tuxmath/trunk/src/titlescreen.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/titlescreen.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/titlescreen.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -94,9 +94,11 @@
void DrawTitleScreen(void);
int HandleTitleScreenEvents(SDL_Event* evt);
void HandleTitleScreenAnimations();
-void ShowMessage(const char* str1, const char* str2, const char* str3, const char* str4);
+void ShowMessage(int font_size, const char* str1, const char* str2, const char* str3, const char* str4);
SDL_Surface* current_bkg(); //appropriate background for current video mode
+/* expecting these to be temporary BML */
+#define playsound(ID) PlaySound(sounds[ID])
/* in audio.c (from tuxtype): */
void PlaySound(Mix_Chunk* sound);
Copied: branches/commonification/tuxmath/trunk/src/transtruct.h (from rev 1657, tuxmath/trunk/src/transtruct.h)
===================================================================
--- branches/commonification/tuxmath/trunk/src/transtruct.h (rev 0)
+++ branches/commonification/tuxmath/trunk/src/transtruct.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -0,0 +1,39 @@
+/*
+
+ transtruct.h
+
+ Description: contains headers for the data structures
+ that would be transferred between the server and the client
+ during the multiplayer LAN game.
+
+ Author: David Bruce ,Akash Gangil and the TuxMath team, (C) 2009
+
+ Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
+
+*/
+#ifndef TRANSTRUCT_H
+#define TRANSTRUCT_H
+
+#define NET_BUF_LEN 512
+#define DEFAULT_PORT 4779
+#define NAME_SIZE 50
+#define MAX_SERVERS 50
+#define MAX_CLIENTS 16
+
+#define MC_USE_NEWARC
+#define MC_FORMULA_LEN 40
+#define MC_ANSWER_LEN 5
+
+#define QUEST_QUEUE_SIZE 10
+
+
+typedef struct _MC_FlashCard {
+ char formula_string[MC_FORMULA_LEN];
+ char answer_string[MC_ANSWER_LEN];
+ int question_id;
+ int answer;
+ int difficulty;
+} MC_FlashCard;
+
+
+#endif
Modified: branches/commonification/tuxmath/trunk/src/tuxmath.c
===================================================================
--- branches/commonification/tuxmath/trunk/src/tuxmath.c 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/tuxmath.c 2009-11-24 23:54:32 UTC (rev 1674)
@@ -19,25 +19,22 @@
2006-2007
*/
-
-#include <stdio.h>
-#include <stdlib.h>
/* (tuxmath.h brings in "gettext.h" and <locale.h> */
#include "tuxmath.h"
#include "setup.h"
#include "titlescreen.h"
+#include "linewrap.h"
+#include <stdio.h>
+#include <stdlib.h>
+
#ifdef WIN32
#define TUXLOCALE "./locale"
#else
#define TUXLOCALE LOCALEDIR
#endif
-//#ifdef LINEBREAK
-#include "linewrap.h"
-//#endif
-
-int main(int argc, char * argv[])
+int main(int argc, char* argv[])
{
const char *s1, *s2, *s3, *s4;
@@ -46,6 +43,8 @@
s3 = bind_textdomain_codeset(PACKAGE, "UTF-8");
s4 = textdomain(PACKAGE);
+ setup(argc, argv);
+
DEBUGMSG(debug_setup, "PACKAGE = %s\n", PACKAGE);
DEBUGMSG(debug_setup, "TUXLOCALE = %s\n", TUXLOCALE);
DEBUGMSG(debug_setup, "setlocale(LC_ALL, \"\") returned: %s\n", s1);
@@ -55,7 +54,6 @@
DEBUGMSG(debug_setup, "gettext(\"Help\"): %s\n\n", gettext("Help"));
DEBUGMSG(debug_setup, "After gettext() call\n");
- setup(argc, argv);
TitleScreen(); /* Run the game! */
cleanup();
return 0;
Modified: branches/commonification/tuxmath/trunk/src/tuxmath.h
===================================================================
--- branches/commonification/tuxmath/trunk/src/tuxmath.h 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/src/tuxmath.h 2009-11-24 23:54:32 UTC (rev 1674)
@@ -22,21 +22,8 @@
#ifndef TUXMATH_H
#define TUXMATH_H
-#include "config.h"
+#include "globals.h"
-#ifdef HAVE_LIBT4KCOMMON
-#include <t4k-common.h>
-#endif
-
-// Translation stuff (now works for Mac and Win too!):
-#include "gettext.h"
-#include <locale.h>
-#define _(String) gettext (String)
-#define gettext_noop(String) String
-#define N_(String) gettext_noop (String)
-
-#include <wchar.h>
-
#include "SDL.h"
#include "SDL_image.h"
@@ -44,8 +31,6 @@
#include "SDL_mixer.h"
#endif
-//#define NOSOUND
-#include "globals.h"
#ifndef HAVE_LIBT4KCOMMON
#define MAX_SPRITE_FRAMES 15
Modified: branches/commonification/tuxmath/trunk/tuxmath.desktop
===================================================================
--- branches/commonification/tuxmath/trunk/tuxmath.desktop 2009-11-24 16:25:12 UTC (rev 1673)
+++ branches/commonification/tuxmath/trunk/tuxmath.desktop 2009-11-24 23:54:32 UTC (rev 1674)
@@ -1,5 +1,5 @@
[Desktop Entry]
-Name=Tux Math
+Name=Tux Math
GenericName=Educational math game
GenericName[de]=Mathe Spiel
GenericName[ru]=Образовательная игра
@@ -15,4 +15,4 @@
Terminal=false
Categories=Education;Math;
## X-SuSE-translate=false
-X-Ubuntu-Gettext-Domain=tuxmath
\ No newline at end of file
+X-Ubuntu-Gettext-Domain=tuxmath
More information about the Tux4kids-commits
mailing list