HOME > > TUTORIALS TABLE OF CONTENTS - - - / - - / - - / - - / - - - Other material for programmers

Delphi tutorial: Typing Tutor. (Level 3)

This is still in a draft form.... it is probably mostly right, but I make no promises just yet!!!

This has good information, and there's a search button at the bottom of the page.

Please don't dismiss it because it isn't full of graphics, scripts, cookies, etc!

Click here if you want to know more about the source and format of these pages.


Dt3d: A Little Typing Tutor

This tutorial is unfinished at the moment... but maybe what is here so far is of some use?

This tutorial develops a little game which is meant to make practicing typing fun.

There's nothing terribly profound in the program. It is in the Level Three category just because you don't get "blow by blow" instructions. The use of an array of TLabel objects is one "advanced" element in this. The relevant issues are intorduced in DT3a if what you find here is less than you want. The tutorial may be most useful as an example of one approach to building an application up from start to finish. Among other things, it illustrates using constants to make programs more robust and more easily modified.

Words appear at the top of a panel, and "fall" towards the bottom of it. If they are typed quickly enough, they disappear, and points are earned. If they reach the bottom, points are lost.

Set up a folder called DD15. (Delphi Demo..)

Start new project. Name the form DD15f1. Save the unit as DD15u1 and the project as DD15.

Add a panel. Name it paWrds. Set Align to Bottom. Make its caption blank.

Set up the following OnResize handler for the form:
procedure TDD15f1.FormResize(Sender: TObject);
begin
  paWrds.height:=dd15f1.clientheight-45;
end;

Just after the form's Uses clause, add
const kMaxWrds=40;
kMaxWrdLen=30;

kMaxWrds will be the maximum index for an array holding words which can appear on the screeen for the user to type. kMaxWrdLen sets the maximum length for the words. Here and elsewhere I've spoken of "words". There's no reason that the program cannot also present short phrases to the user.

To the var clause add

saWrds:array[0..kMaxWrds] of string [kMaxWrdLen];

Set up the following. Make bLastWrd as a global variable.
procedure FillsaWrds;
begin
saWrds[0]:='fred';
saWrds[1]:='red';
saWrds[2]:='was';
saWrds[3]:='saw';
saWrds[4]:='dare';
saWrds[5]:='veer';
saWrds[6]:='sweet';
saWrds[7]:='dear';
saWrds[8]:='drag';
bLastWrd:=8;
end;

For this simple demo program, you can put the call to FillsaWrds in the form's OnCreate handler.

Add a button, two labels, two timers and an edit box to the form. Set them as follows...
Button: Name buStart.
Labels: Names: laLeft, laScore. (laTimeLeft will count down to show time remaining in round.)
Timers: Names: tiSeconds, tiFallInt. Disable both. Intervals: 200 (for now) and 100. (tiSeconds's interval will be 1000 in the finished version of the program.)
Edit box: Name: edIn. (This will be where user types in the words to "erase" them from the panel.)

Thoughout the rest of this, various variables will be introduced. They will all have to be declared, of course, but I won't always tell you explicitly to do that.

Make tiSeonds's OnTimer event say...
dec(bTimeLeft);
laLeft.caption:=inttostr(bTimeLeft);
if bTimeLeft=0 then tiSeconds.enabled:=false;

and then make the start button's OnClick handler say...
tiSeconds.enabled:=true;
tiFallInt.enabled:=true;
bTimeLeft:=60;
laLeft.caption:=inttostr(bTimeLeft);
bScore:=0;
laScore.caption:=inttostr(bScore);
iOnP:=-1;(*Number of words currently on panel.. has to start
   at -1 to help make first filling of arrays go well.*)

(Declare iOnP as an integer type variable.)

You should now get a count down if you click on buStart. When that is working right, you may want to set tiStart's interval to 1000 so that the countdown is in seconds. This is an example of how with a little thought, often you can easily make programs behave oddly to facilitate debugging.

Before we can write more code, you need to understand some more variable and arrays. The program will use the following futher constants (Just add to existing const clause)...
kMaxWrdLen=30;
kOnMaxPoss=12;

and the following new variables and arrays...
bWOnMax,bWOnMin:byte;
baWOnXL,baWOnYT,baWOnXR,baWOnYB:array [0..kOnMaxPoss] of word;
saWOn:array [0..kOnMaxPoss] of string [kMaxWrdLen];

kMaxWrdLen sets the longest word or phrase allowed
kOnMaxPoss sets the maximum index for all arrays keeping track of words currently "falling" down the panel.

bOnMax, bOnMin are variable holding the maximum and minimum number of words to be on the panel at this time. (The "extra layer" has been added so that "easy" versions can be provided simply by changing what is in these variables.)

saWOn, waWOnXL bOnWYT, baWOnXR and bOnWYB hold the words currently on the panel and their positions. (XL/YT: X cood, left side/ Y cood, top. XR/YB: Right/ Bottom)

You might be wondering about the "complicated" business of declaring, say, kOnMaxPoss, and then using that when setting up the arrays, etc. If you are careful to use kMaxOnPoss in EVERY relevant place, instead of hardcoding with 12 (or whatever other figure you feel is right), then changing your mind about something like that is as simple as changing "const kMaxPoss=12" to const "kMaxPoss=20". It imposes disciplined thinking, too.

(Apologies for some inconsistency in my variable names. In some cases an array of bytes for Footle might be called saFootle, and yet something similar might be asFootle2. Sorry!)

Now we are ready to start developing tiFallInt's OnTimer.

First, to OnFormCreate add...
bWOnMax:=8;
bWOnMin:=4;

Then make a start with...
procedure TDD15f1.tiFallIntTimer(Sender: TObject);
var c1:byte;
  procedure AddWord;
  begin
  inc(iOnP);
  laLeft.caption:='write AddWord';
  end;

begin (*tiFallIntTimer*)
while iOnP<bWOnMin do AddWord;
for c1:=0 to iOnp do begin
  laScore.caption:='write DropWord';
end;
end;


"AddWord" will add another falling word to the panel and take care of all the internal variable changing required. DropWord will erase the word from where it was, and if it hasn't hit to bottom (in which case remove and do things to score) put it back on, further down the screen. Next, once this event handler is finished, all that will be left is the handler for edIn, which will look at what the user has typed, and remove words if appropriate.

Just a little aside that may give you "misery loves company" consolation the next time you do something like what I did....

On my first attempt with this, I left out the "inc(iOnP);" in AddWord. This make my system lock up... you should be able to see why. Delphi had autosaved my project... but the text file that gave rise to what you are reading now had not been saved since the....


...
saWrds[7]:='dear';
saWrds[8]:='drag';
bLastWrd:=8;
end;

a long way up this file.. and everything between there and here had to be re-generated. I hope I haven't left bits out! Get in touch if I have!

Back to developing DD15...

To begin with, we are going to write a program with a serious flaw: Some of the falling words may overlap one another. Before the code was written, this problem was anticipated, and a way out was foreseen, but getting the words on the screen etc, even if overlapping, was chosen as a first objective.

If the overlap problem had not been anticipated, the TextOut and TextWidth Delphi words might have been used. I could not, however, find a TextHeight word, which I though I needed. Not using TextOut meant putting the words on labels on the panel, and elegant management of them requires an array of labels. (There is more on creating arrays of objects in DT3a if what follows doesn't give you all you want. By the way, sorry: Unless I've edited that since writing this, there's an error about a quarter of the way through that. Where it says...

"Use the Object Inspector to create an OnCreate event handler for TEdit",

it should say

"Use the Object Inspector to create an OnCreate event handler for Demof1")

Just before going on with DD15 as created, it is worth mentioning another "solution" that was considered and rejected. (It was too crude, and not flexible enough.) It would have been easier to write a program that divided the panel into a number of columns and only allowed a single word in each column.

So! Back to DD15: Set up an array of Tlabel objects by adding, just after "public" in the TDD15f1 = class definition....

alaWrd:array [0..kOnMaxPoss] of TLabel;

(N.B.: This is not exactly the approach used in my "Creating an array of edit boxes" tutorial.)

Modify the form's OnCreate, making it...
procedure TDD15f1.FormCreate(Sender: TObject);
var c1:byte;
begin
FillsaWrds;
bWOnMax:=8;
bWOnMin:=4;
for c1:=0 to bWOnMax do begin
  alaWrd[c1]:=TLabel.create(self);
  alaWrd[c1].parent:=paWrds;
  alaWrd[c1].hide;
  end;
end;


Add another constant called kTopGap and set it to 45. Replace the 45 in OnFormResize with kTopGap, e.g....

paWrds.height:=dd15f1.clientheight-kTopGap;

(We may not need this after all... After suggesting this change and putting it in my DD15, I discovered I didn't need it... yet. There's no harm in it, though, and as I said before, using such things is a good idea. Now you have only to change kTopGap... to change the space for buttons and text above the panel with the (to be) falling words.)

Make AddWord the following... as a start, only...
inc(iOnP);
saWOn[iOnP]:='Wrd'+inttostr(iOnP);
alaWrd[iOnP].caption:=saWOn[iOnP];
waWOnXL[iOnP]:=0+iOnP*60;
waWOnYT[iOnP]:=0;
alaWrd[iOnP].left:=waWOnXL[iOnP];
alaWrd[iOnP].top:=waWOnYT[iOnP];
waWOnXR[iOnP]:=alaWrd[iOnP].width;
waWOnYB[iOnP]:=alaWrd[iOnP].height;
alaWrd[iOnP].show;

That should put Wrd0, Wrd1, Wrd2, Wrd3 and Wrd4 across the top of the panel. If things are not working well for you, it may pay to add alaWrd[iOnP].color:=clRed to the OnCreate code... it makes it easier to see empty labels.

We'll have to come back to AddWord later, but that will do for now.

Next: Flesh out the "make it fall" part of tiFallIntTimer as follows. Note the temporary line is only in the program until such time as we provide the "deal with words that hit the bottom of the page" code.

Make the form's height 230 before running the following.
begin (*tiFallIntTimer*)
while iOnP140 then waWOnYT[c1]:=140;
  alaWrd[c1].top:=waWOnYT[c1];
  end;
end;


Now we'll work in the "Deal with user input" part of the program.

To the buStart OnCLick event handler add....

ebIn.setfocus;

Now create an OnKeyPress event for ebIn...
procedure TDD15f1.ebInKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var c1:byte;
begin
if key=13 then begin
  sTmp:=ebIn.text;
  for c1:=0 to iOnP do begin
    if sTmp=saWOn[c1] then begin
      saWOn[c1]:='';
      alaWrd[c1].caption:=saWOn[c1];
      end;
    end;
  ebIn.text:='';
  end;

This is just a start, but it should at least "zap" words you type.. even if they have got to line 140. (Be sure to type, say, "Wrd2", NOT "Wrd2".)

At this stage in writing the tutorial, I had an unpleasant sinking feeling. At this stage I realised that there was a flaw in my basic design. If I had not been writing a tutorial, I would have gone back and changed things. However, I didn't relish changing not only the program, but also the tutorial, so I'm going to use a messy work-around. Those who spot the inelegance which could have been avoided may award themselves a Gold Star. The flaw also makes a nonsense of the variable bWOnMax I set up. I'd had in mind that the number of words on the screen would vary between bWOnMin ans bWOnMax, but that will now be difficult, and you'll only have bWOnMin words on the screen at any time. Oops!

Well... so far so good... but here I've run out of time for the moment. Email me a "Finish Dt3d, please" email to encourage me to finfish it!!

   Search this site or the web        powered by FreeFind
 
  Site search Web search
Site Map    What's New    Search


Click here if you're feeling kind! (Promotes my site via "Top100Borland")
Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows pc) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!

Click here to visit editor's freeware, shareware page.


Link to Tutorials main page
Here is how you can contact this page's author, Tom Boyd.


Valid HTML 4.01 Transitional Page WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....