-- Sums.exw -----------------------------------------------------------------
-- A beginner's program to present randomly generated "sums" on screen for
-- a child user to respond to. The "sums" involve integers in the range
-- 1 to MAX_INTEGER = 12.
-- --------------------------------------------------------------------------
-- In a Euphoria 3.1 program, text lines or parts of text lines preceded by
-- by 2 hyphens, thus: "-- text", are comments.
-- This program is outrageously over-commented, to give programmers who don't
-- use Euphoria(*) (nor the EuWinGUI(**) wrapper) some idea of what's going
-- on. If you successfully plough through the rest of this listing, I hope
-- you will end up by agreeing that Euphoria (and the EuWinGUI wrapper) make
-- writing a simple windows' program like this one a fairly clear and
-- understandable business. I have found them equally good for writing more
-- complex programs as well.
-- (*),(**) : info about Euphoria and EuWinGUI after end of listing.
-- --------------------------------------------------------------------------
-- (c) Fred Mangan (2011)
-- Skeleton's code generated by "The Window Designer for EuWinGUI Applications"
--(C)2001-2006 by Andrea Cini.
--***************************************************************************
-- Start of program code:
-- read in the following "include" files (like header files in C):
include EuWinGUI.ew -- for the EuWinGUI Library (wrapper)
include Utility.e -- for function trim()
include Get.e -- for function value()
include file.e -- for current_dir()
--***************************************************************************
-- declare handles for main window controls; the main window is automatically
-- assigned the handle WinHwnd by EuWinGUI, so is not declared here:
atom SelectCalcModeGroup,AddRadio,SubtractRadio,MultiplyRadio,DivideRadio,
MixedRadio,ProblemStatementText,UserAnswerEdit,DoWhatText,SummaryLabel,
MoodPicture,MusicTag
--***************************************************************************
-- declare handles of our 2 programmer-created fonts:
atom FontA,FontB -- the fonts will be created later
--***************************************************************************
--*** This is a good place to define constant values used throughout the
-- program. Constants are effectively global. This sort of declaration makes
-- it easy later on to change a value across the whole of your program, by
-- simply altering its value here. It can also help make subsequent program code
-- less cryptic, if you invent good descriptive names for constants.
-- Assorted constants: customarily named using Caps:
constant
RPATH = "Resources\\", -- path to resource files
-- file for main music theme:
MUSIC = RPATH & "amclassical_joplin_the_entertainer_rag.wav",
MUSIC_DURATION = (4.55 * 60)* 1000, -- (aprox., millisecs)
HAPPY_FSPEC = RPATH & "HappyFace.bmp", -- other files used
SUCCESS_SOUND = RPATH & "Success.wav",
FAILURE_SOUND = RPATH & "Failure.wav",
HTM_LISTING = ".\\SCode.htm",
WIN_SIZE = {400,270}, -- size of main window
WIN_POSN = {floor((ScreenWidth()-WIN_SIZE[1])/2),-- position for it
floor((ScreenHeight()-WIN_SIZE[2])/2)
},
MAX_INTEGER = 12, -- max integer value for any number in this game
ADD_MODE = 1, -- the calculation modes: 1 ... 5
SUBTRACT_MODE = 2,
MULTIPLY_MODE = 3,
DIVIDE_MODE = 4,
MIXED_MODE = 5,
MUL_CHAR = 215, -- math multiply character
DIV_CHAR = 247, -- math divide character
-- formats for prompting user with "sums":
PROMPT = { "%2d + %2d =", -- adding
"%2d - %2d =", -- subtracting
"%2d " & MUL_CHAR & " %2d =", -- multiplying
"%2d " & DIV_CHAR & " %2d =" -- dividing
},
-- a format string, to display the current state of the game to user:
SUMMARY_FMAT = " %d answers correct out of %d questions asked."
--*** Variables declared here will also be global. Programs using
-- Windows(r) have to be able to "remember stuff" in between the
-- occurence of user initiated events. Use of global variables like
-- these is a way to achieve this.
-- Declare variables used globally, and initial them as needed:
sequence IntPair IntPair = {0,0} -- to hold integer pairs
integer CalcMode -- to flag calculation mode
integer NGamesPlayed NGamesPlayed = 0 -- to tally games played
integer UserScore UserScore = 0 -- to tally user's score: 1/game
integer GameOn GameOn = False -- to flag game is in progress
--*** Next come the routines (functions and procedures) written by the
-- programmer to make this particular application work.
-- 1. Section has a hard-to-classify routine --------------------------------
procedure ShowMoodPicture(integer delay)
-- This does things necessary for display of the MoodPicture
-- Entry: delay = time for which to display picture (ms).
SetPicture(MoodPicture,Picture,HAPPY_FSPEC) -- load pic to control
SetDim(MoodPicture,90,60) -- make sure it fills control
SetVisible(UserAnswerEdit,False) -- vanish the user's edit
SetVisible(MoodPicture,True) -- visualise the picture
PlaySnd(SUCCESS_SOUND) -- play the success sound
SetWinTimer(delay) -- time for which to view it
end procedure -- ShowMoodPicture
-- 2. Section deals with setting up a problem for user to solve -------------
procedure GenerateIntegerPair()
-- Randomly generates sequence IntPair such that
-- IntPair = {n[1], n[2]}; 1 <= n[2] <= n[1] <= MAX_INTEGER, all integer.
-- This arrangement (next) caters for subtraction so it won't produce -ve results.
IntPair[1] = rand(MAX_INTEGER-1) + 1 -- range: 2...MAX_INTEGER
IntPair[2] = rand(IntPair[1]) -- range: 1...n[2]
end procedure -- GenerateIntegerPair
procedure AdjustIntegerPairForDivision()
-- Calculate a random n[2] that will divide evenly into n[1]
-- (a requirement if division is to produce an integer result)
object x
integer max
max = floor(IntPair[1]/2) -- max value dividend n[2] may have
x = 1.1 -- x now not an integer
while not integer(x) do -- loop until x holds an integer value
-- this loop is bound to end, if only with a division by 1
IntPair[2] = rand(max) -- range: 1...max
-- do a trial division, save it in x
x = IntPair[1] / IntPair[2] -- calculate new x to test
end while
end procedure -- AdjustIntegerPairForDivision
procedure SetUpForNewProblem()
-- Creates a problem to be offered to user and displays it.
-- The sort of "sum" is dictated by which radio button is checked.
-- Calls GenerateIntegerPair() and AdjustIntegerPairForDivision()
sequence text_to_display
-- set the calculation mode, CalcMode, for current "sum"
if IsChecked(AddRadio) then
CalcMode = ADD_MODE -- mode: adding
elsif IsChecked(SubtractRadio) then
CalcMode = SUBTRACT_MODE -- mode: subtracting
elsif IsChecked(MultiplyRadio) then
CalcMode = MULTIPLY_MODE -- mode: multiplying
elsif IsChecked(DivideRadio) then
CalcMode = DIVIDE_MODE -- mode: dividing
elsif IsChecked(MixedRadio) then
CalcMode = rand(4) -- mode: one of 4 above, random
end if
GenerateIntegerPair() -- basic pair for current "sum"
-- a special case is division; deal with it:
if CalcMode = DIVIDE_MODE then
-- adjust pair to have division give an integer result
AdjustIntegerPairForDivision()
end if
-- display the "sum" to user:
text_to_display = sprintf(PROMPT[CalcMode],{IntPair[1],IntPair[2]})
SetText(ProblemStatementText,text_to_display)
SetText(UserAnswerEdit,"") -- blank the edit field
Activate(UserAnswerEdit) -- focus is now on this field
end procedure -- SetUpForNewProblem
-- 3. Section deals with users response to a "sum" problem ------------------
procedure ServiceTimeout()
-- On timeout does the following things
SetVisible(MoodPicture,False) -- vanish the MoodPicture, if displayed
SetVisible(UserAnswerEdit,True) -- visualise the user edit
SetWinTimer(0) -- turn off the timer
if not GameOn then -- has user started playing?
-- user has not started playing
PlaySnd(MUSIC) -- play theme MUSIC
SetWinTimer(MUSIC_DURATION) -- trigger for possible MUSIC replay
end if
SetUpForNewProblem() -- prepare a new problem for next game
end procedure -- ServiceTimeout
function Validate(integer user_answer)
-- Check user's arithmetic
integer ok, right_answer
-- cumputer needs to know the right_answer: calculate it:
if CalcMode = ADD_MODE then
right_answer = IntPair[1] + IntPair[2]
elsif CalcMode = SUBTRACT_MODE then
right_answer = IntPair[1] - IntPair[2]
elsif CalcMode = MULTIPLY_MODE then
right_answer = IntPair[1] * IntPair[2]
elsif CalcMode = DIVIDE_MODE then
right_answer = IntPair[1] / IntPair[2]
end if
if user_answer = right_answer then
-- user has correct answer
ok = True
else
-- user has wrong answer
ok = False
-- set up to show correct answer
SetText(UserAnswerEdit,sprintf("%d",right_answer))
end if
return ok -- tell caller the outcome of user response
end function -- Validate
procedure ComputeUserResult()
-- Processes user's response, to be found in UserAnswerEdit control
-- Calls function Validate()
sequence response,v
atom user_answer
response = trim(GetText(UserAnswerEdit)) -- fetch and trim response
if length(response) > 0 then
-- possible valid input; get its numeric value
v = value(response)
if v[1] = GET_SUCCESS then
-- user has a number of some sort
user_answer = v[2] -- get users numeric answer
NGamesPlayed += 1 -- advance game counter
if integer(user_answer) then
-- have legal (integer) input; test it in Validate()
if Validate(user_answer) then
-- user has correct answer
UserScore +=1 -- advance user's score
ShowMoodPicture(2000) -- show success picture
else
-- user has a wrong answer
PlaySnd(FAILURE_SOUND)
SetWinTimer(2000)
end if
else
-- user typed a decimal number - count it as wrong
PlaySnd(FAILURE_SOUND)
end if
else
-- user typed rubbish: just put him/her back into Edit, no penalty
SetText(UserAnswerEdit,"")
Activate(UserAnswerEdit)
end if
else
-- user's field is empty: just put him back into it, with no penalty
Activate(UserAnswerEdit)
end if
-- report state-of-play summary:
SetText(SummaryLabel,sprintf(SUMMARY_FMAT,{UserScore,NGamesPlayed}))
-- suppress playing of music while game is in progress
GameOn = True
end procedure -- ComputeUserResult
procedure ProcessUserInput()
-- Over-all control of checking user response and of scoring
if EventItem = KEY_ENTER then
-- process user's (possible) response in UserAnswerEdit
ComputeUserResult()
-- you might expect a call to SetUpForNewProblem() here;
-- but it was found better to transfer the call to
-- procedure ServiceTimeout(), so that theme MUSIC plays
-- only at the start of the first game and not (distractingly)
-- during actual game play.
else
-- user is just typing ... let him finish doing so by doing nothing
end if
end procedure -- ProcessUserInput
-- 4. Section handles program terminating routine ---------------------------
procedure TerminatingRoutine()
sequence message
integer dummy
message = "Do you want to view this program's source code?"
if AskMsg(message, "View Source Code") then
-- call up htm containing a copy of the program code:
RunApp("Explorer.exe",HTM_LISTING) -- note: needed full form
end if
end procedure -- TerminatingRoutine
-- 5. Section handles most of program initialling ---------------------------
procedure Initialize()
-- Does initialling of main window (system handle: WinHwnd) and its controls
-- and establishes conditions for first game.
-- Much of the code in this procedure was generated by the "Designer". I have
-- tweaked it here and there.
CloseEventEnabled = True
-- create programmer's fonts; fonts can be external if wished
-- name, pt, bold, italic, u-line
FontA = NewFont("FN_DEFAULT",48, True, False, False) -- a real big font
FontB = NewFont("FN_DEFAULT",12, True, False, False) -- a bit of bolding
-- create main window:
WindowType = NoMaxWin
ShowFlag = False
Window("Starting Arithmetic",WIN_POSN[1],WIN_POSN[2],WIN_SIZE[1],WIN_SIZE[2])
ShowFlag = True
SelectCalcModeGroup = Control(Group,
" Select the sort of Tables to do ",
10,10,
370,85)
AddRadio = Control(Radio,"ADDITION",80,30,110,25)
SubtractRadio = Control(Radio,"SUBTRACTION",200,30,110,25)
MultiplyRadio = Control(Radio,"MULTIPLICATION",20,60,110,25)
DivideRadio = Control(Radio,"DIVISION",140,60,110,25)
MixedRadio = Control(Radio,"MIXED MODE",260,60,110,25)
ProblemStatementText = Control(Text,"12 + 12 =",40,110,210,50)
UserAnswerEdit = Control(Edit,"24",250,104,90,55)
-- MoodPicture will overlie UserAnswerEdit to show a picture in its place
MoodPicture = Control(Picture,"",250,104,90,60)
DoWhatText = Control(Text,
"TYPE YOUR ANSWER THEN PRESS THE ENTER KEY",
40,170,
280,20)
SummaryLabel = Control(Label,sprintf(SUMMARY_FMAT,{0,0}),50,200,280,15)
MusicTag = Control(Label,
"Music: The Entertainer Rag - recording (c) amclassical.com",
50,WIN_SIZE[2]-50,
280,20)
-- apply programmer's fonts selectively:
-- (anything else printed on WinHwnd or its controls will use EuWinGUI's
-- FN_DEFAULT font, size 12 pt. plain, by default).
SetFont(ProblemStatementText,FontA)
SetFont(UserAnswerEdit,FontA)
SetFont(SummaryLabel,FontB)
-- set colouring to enhance appearance of some controls:
-- (this was all done visually in the "Designer")
SetColor(SelectCalcModeGroup,CL_WHITE,CL_RED)
SetColor(AddRadio,CL_WHITE,CL_BLUE)
SetColor(SubtractRadio,CL_WHITE,CL_BLUE)
SetColor(MultiplyRadio,CL_WHITE,CL_BLUE)
SetColor(DivideRadio,CL_WHITE,CL_BLUE)
SetColor(MixedRadio,CL_WHITE,CL_BLUE)
SetColor(ProblemStatementText,CL_RED,CL_DEFAULT)
SetColor(UserAnswerEdit,CL_RED,CL_DEFAULT)
SetColor(SummaryLabel,CL_WHITE,CL_BLUE)
SetVisible(WinHwnd,True) -- make the main window visible
SetCheck(AddRadio,True) -- have at least one radio button checked
ShowMoodPicture(2000) -- display this
SetUpForNewProblem() -- initial the first "sum"
end procedure -- Initialize
-- 6. Section deals with the EventLoop --------------------------------------
--*** Every program skeleton generated by the "Designer" has an EventLoop()
-- procedure. It contains an endless closed "while" loop in which occurence
-- of user-generated events is tested for. When one occurs, the programmer
-- can modify and add to code in the loop so that loop code will test for
-- occurence of events of interest to the particular program being written
-- and will call procedures or functions accordingly.
-- The EventLoop() in this program responds to only a few events since this
-- is a relatively simple program. I have left unused code in the loop in
-- place so you can see something of the range of events that an EventLoop()
-- can respond to.
procedure EventLoop()
while True do
WaitEvent()
if Event = Click then -- if there is a left-click on the mouse
-- if any radio button is clicked, start a new problem
-- (code here is, perhaps, a bit inelegant, but it is clear).
if EventOwner = AddRadio then
SetUpForNewProblem()
elsif EventOwner = SubtractRadio then
SetUpForNewProblem()
elsif EventOwner = MultiplyRadio then
SetUpForNewProblem()
elsif EventOwner = DivideRadio then
SetUpForNewProblem()
elsif EventOwner = MixedRadio then
SetUpForNewProblem()
end if
end if
if Event = RClick then -- our program is not interested in this event
-- (right-click with mouse)
if EventOwner = WinHwnd then
end if
end if
if Event = Release then -- our program is not interested in this event
-- (release of mouse left-button)
if EventOwner = WinHwnd then
end if
end if
if Event = RRelease then -- our program is not interested in this event
-- (release of mouse right-button)
if EventOwner = WinHwnd then
end if
end if
if Event = Move then -- our program is not interested in this event
-- (movement of the mouse)
if EventOwner = WinHwnd then
end if
end if
if Event = Key then -- if a keyboard key was pressed
if EventOwner = UserAnswerEdit then
ProcessUserInput() -- call our key handling routine
end if
end if
if Event = Restore then -- our program is not interested in this event
-- (restoring program from toolbar)
if EventOwner = WinHwnd then
end if
end if
if Event = Close then -- is it a "Close" event?
-- it is a "Close"
if EventOwner = WinHwnd then -- was it generated at main window?
-- it was generated by main window, so ...
TerminatingRoutine()
return -- ... quit this procedure
-- this gets us out of the endless loop and will let the calling
-- procedure, WinMain(), close the program.
end if
end if
if Event = Time then -- is it a WinTimer (timeout) event?
-- it is a WinTimer event, so ...
ServiceTimeout() -- ... call our timeout handling routine
end if
if Event = HotKey then -- our program is not interested in this event
-- (a press on the Tab key)
end if
end while
end procedure -- EventLoop
--***************************************************************************
-- 7. Section deals with the WinMain() procedure ----------------------------
procedure WinMain()
--*** The "Designer" always generates code for this routine. In this program
-- it has been modified to do 3 things as commented below
Initialize() -- 1. do all main initialling of program
EventLoop() -- 2. run the EventLoop()
CloseApp(0) -- 3. we're finished; close the program
end procedure -- WinMain
--***************************************************************************
WinMain() -- fire-up the program by calling WinMain() procedure
-- end of program code ---------------------------------------------------------
-- SOME EXPLANATION OF EUPHORIA AND EUWINGUI:
-- (*) This program is coded for EUPHORIA version 3.1. Version 4.0.0 has
-- recently been released containing many enhancements and more directed
-- towards programming for Windows; it may still be a bit buggy.
-- Version 3.1 is probably easier for beginners to use and is very stable.
-- Both versions are available to run under Windows and under some popular
-- versions of Unix (FreeBSD, Linux, etc.).
-- Euphoria is free, Open Source in the public domain; it can be used more
-- or less by anyone for any purpose.
-- Other extensions available for Euphoria include:
-- * OOP wrappers: Euphoria itself is a procedural language but can be extended
-- to use OOP definitions and methods via a number of OOP wrappers.
-- * Euphoria to C translator: where top speed is essential, critical code
-- can be translated to C.
-- * Database: The "Euphoria Database System" (EDS) can be incorporated
-- into Euphoria programs. EDS provides indexed database files which
-- can be created, interrogated and retrieved from using near English-
-- like statements. An EDS data file can hold about 2GB of data. There
-- is no limit on the number of data files you can have. EDS Tables
-- can contain any or all of the data object(s) supported by Euphoria.
-- * Binding: all Euphoria programs can be "bound" to a copy of the
-- of the Euphoria Interpreter to produce a directly executable (exe)
-- file which will run the program without having to invoke the
-- interpreter by name. In such files your code is "shrouded" and
-- can't be read or tinkered with by users.
-- (**) EuWinGUI.ew is an easy-to-use Library file giving the programmer
-- simplified access to the Windows' API, or the Unix equivalent.
-- Euphoria has a more advanced wrapper (Win32Lib.ew). It is for more
-- ambitious or advanced Windows programmers.
--*** The "The Window Designer for EuWinGUI Applications" (the "Designer"
-- from now on) is a simple IDE with which you can create the main window,
-- the secondary windows (if any), and lay out the controls (buttons, text,
-- edit fields, and so on) to be used by your program. The IDE can save your
-- design for future development, and also can output "skeleton" code for
-- your program written as Euphoria statements, and as statements which
-- EuWinGUI.ew and EuWinGUI.dll can understand.
-- More information and free downloads at RapidEuphoria.com.
Conversion to HTML by PC2HTM.EXE