René's Avigo Page: The Numbers Game

The Numbers Game – Les Chiffres

This is my second try on the Avigo system. It is actually a port from a
Java applet and then again from very old C routines I once wrote. The
problem is based on a TV show in french channel one.

Numbers-Capture.jpg


In the above picture, the problem is to form the 179 with 3, 5, 6, 6,
7, and 100 not using any number twice. The allowed operations are +, -,
  • and /. You can either let the computer solve the problem, and the
    solution in the captured image has been found by the computer. Or you
    can try to solve yourself, using the right part of the panel. This is
    done by selecting two numbers on the list of available numbers and
    pressing an arithmetic button. Other screen elements are four buttons
    and one label “New”.

    The code consists of two header files and two C files. You can download the application with source in zipped form from here: numbers.zip


Screen Layout

The screen layout consists of several elements. Here is a list from the code



PNUMBERFIELD * INumbers6;
PNUMBERFIELD * ITarget;
PBUTTON * NewProblem;
PBUTTON * NewTarget;
PBUTTON * Solve;

PBUTTON * Retry;
PBUTTON * Plus;
PBUTTON * Minus;
PBUTTON * Div;
PBUTTON * Mult;
PLISTER * List;
PLABEL * Output5;
PLABEL * New;


The seven top elements are of type NUMBERFIELD. Clicking on one of them
displays a number picker, which is a small calculator. The screen is
repainted afterwards. It might be possible to save the image below the
picker, but I did not find the code for this. I did not want to invest
too much time for this application. The code to get and display a
number field is this:


/* Generate six number labels at the top */
for (i=0; i<6; i++)
{INumbers[i]=(PNUMBERFIELD *)CreateNumberField(
id++,
5+25*i, row,
(unsigned
char *)(Numbers+i),
DTINTEGER,
MK_FAR_PTR(“Input Number”));
INumbers[i]->right=25+25*i;

LoadBank(&(dsk->insert));
dsk->insertVOID_PTR)dsk,INumbers[i]);
}

/* Generate a target label centered below */
row+=20;
ITarget=(PNUMBERFIELD *)CreateNumberField(

id++, 65, row,
(unsigned char *)(&Target),
DTINTEGER, MK_FAR_PTR(“Input Target”;
ITarget->right=95;

LoadBank(&(dsk->insert));
dsk->insertVOID_PTR)dsk,ITarget);


As you see, there is a row variable, which is allows later insertion or rearrangement of screen elements. I am unsure, if the LoadBank?

operations are needed every time. However, they seem not to harm. By
the way, the objects are inserted into a DESKBOX object dsk, which is
the same as in the Lotto example.

Then we have six labels at the left. These are generated with the
CreateLabel function, just as the Number Fields. I won’t explain the
buttons either, because they are straightforward. The only change is a
command number, which the CreateButton function takes. This number will
be used in the event handler.

A more complicate topic is the list of available numbers at the right
panel. The Avigo SDk does not support a comfortable list element. It
only provides a LISTER object, which reacts on mouse buttons and calls
a redraw routine for each item. You have to override both event
handlers and take the proper action. My solution is this:





/* event handlers /
extern BANKED void list_writeItem (VOID_PTR view,
unsigned char x, unsigned char y, unsigned short item);
extern BANKED void list_pen (VOID_PTR view,
unsigned char x, unsigned char y);
int Available6,Marked6;
int NAvailable;
void setAvailable ()
/
copy numbers from the Numbers field */
{ int i;

for (i=0; i<6; i++)
{ Available[i]=Numbers[i];
Marked[i]=0;
}

NAvailable=6;
}
int nmarked ()
/* get number of marked objects */
{ int i,n=0;
for (i=0; i<NAvailable; i++)
if (Marked[i]) n++;
return n;
}
void list_writeItem (VOID_PTR view,
unsigned char x, unsigned char y, unsigned short item)

/* event handler for writing */
{ char s16;

PLISTER *p; p=(PLISTER *)view;
if (item>=NAvailable) return;
NumericToStr((double)Available[item],s,DTINTEGER);
SetFontType(PRPFONT11N);

WriteString(x,y,s,Marked[item]);
}
void unmarkall ()

/* unmark all numbers */
{ int i;
for (i=0; i<NAvailable; i++) Marked[i]=0;
}
void list_pen (VOID_PTR view, unsigned char x, unsigned char y)

/* event handler for pen touches */
{ PLISTER *p; p=(PLISTER *)view;
int i;
x=0;
i=(y-p->top)/((p->bottom-p->top)/6);

if (i>=0 && i<NAvailable)
{ if (nmarked()>=2)
{ if
(!Marked[i]) unmarkall();

}
Marked[i]=!Marked[i];
}
PLISTER_draw(view);
}
void availableremove (int j)

/* remove number at j from available numbers */
{ int i;
if (j>=NAvailable) return;

for (i=j; i<NAvailable-1; i++)
{ Available[i]=Available[i+1];
Marked[i]=Marked[i+1];
}

NAvailable—;
}
void availableappend (int h)

/* append number h to available nubmers */
{ if (NAvailable>=6) return;
Available[NAvailable]=h;
Marked[NAvailable]=0;
NAvailable++;
}
void available (int command)
/* called from the main event handler (buttons ,-,*,/) */
{ if (nmarked()!=2) return;
int h1,h2,i1,i2,f,i,h;
long L;
f=0;
for (i=0; i<NAvailable; i
+)
{ if (Marked[i])
{ if
(f) i2=i;
else
i1=i;
f++;
}
}
h1=Available[i1]; h2=Available[i2];
if (NAvailable==6) clear();
switch (command)
{ case cmdPlus :
h=h1+h2;
writesolution(6-NAvailable,h1,h2,’+’,h);
break;
case cmdMinus :
if
(h1<h2)
{ h=h2-h1;
writesolution(6-NAvailable,h2,h1,’-’,h);
}
else
if (h1>h2)
{ h=h1-h2;
writesolution(6-NAvailable,h1,h2,’-’,h);
}
break;
case cmdMult :
L=h1*h2;
if
(L>30000l) return;
h=h1*h2;
writesolution(6-NAvailable,h1,h2,’*’,h);
break;
case cmdDiv :
if
(h1>h2)
{ if
(h1%h2!=0) return;
h=h1/h2;
writesolution(6-NAvailable,h1,h2,’/’,h);
}
else
if (h1<h2)
{ if
(h2%h1!=0) return;
h=h2/h1;
writesolution(6-NAvailable,h2,h1,’/’,h);
}
else
if (h1==h2)
{ h=1;
writesolution(6-NAvailable,h2,h1,’/’,h);
}
else
return;
}
availableremove(i2);
availableremove(i1);
availableappend(h);
}

This is some code! The last function, however, is already an interface,
which connects the +, -, * and / buttons to the available numbers list.
So it does not count. You have to assign the handler functions.


List=(PLISTER *)CreateLister?(
id++,95,row,120,row+5*15,6);
LoadBank(&(dsk->insert;
dsk->insertVOID_PTR)dsk,List);
BankedAssign(List->writeItem,list_writeItem);
BankedAssign(List->penDownAct,list_pen);


So you only insert the List field as a complete object.

The last problem is to draw the lines, which seperate the panels. this is done in the panel redraw function.

void test_draw (VOID_PTR view)
{ /* the pointer is really a point to a DESKBOX /
PDESKBOX *dsk;
dsk=(PDESKBOX *)view;
PDESKBOX_draw(view); /
default, clears the view */
DrawLine(0,line1,160,line1,DRAW_BLACK);
DrawLine(0,line2,160,line2,DRAW_BLACK);
DrawLine(80,line1,80,line2,DRAW_BLACK);
}


The line1 and line2 variables are set by the generation routine, which inserts the screen elements from top to bottom.

After a first test, it was clear that a complete redraw of the screen
is to slow. So separate redraw functions had to be written for the
numbers, the target, the solution and the available number list.


void solution_draw ()
/* redraw the solution strings */
{ int i;
for (i=0; i<5; i++)
{ PVIEW *p;
p=(PVIEW *)Output[i];
FillRect(p->left,p->top,
p->right,p->bottom,

DRAW_WHITE);
PLABEL_draw((VOID_PTR)Output[i]);
}
}
void clearSolution ()

/* clear the solution area and redraw it */
{ clear();
solution_draw();
}

void available_draw ()

/* redraw the available nubmers */
{ PLISTER_draw((VOID_PTR)List);
}
void numbers_draw ()

/* redraw the numbers */
{ int i;
for (i=0; i<6; i++)

PNUMBERFIELD_draw((VOID_PTR)INumbers[i]);
}
void target_draw ()

/* redraw the target number */
{ PNUMBERFIELD_draw((VOID_PTR)ITarget);
}

The Solution strings are contained in the other source file, by the way.

Solving the problem


This routine is ported from the Java version. However, Java on a
Pentium 166 detects if any solution within seconds. I did not even
bother for multithreading. On the Avigo, detecting that there is no
solution takes minutes.

So I tried to make this interruptable by the user. I did it by calling GetEvent?
at regular intervals (every 1000 trials). However, this only makes the
application slower, since I am catching Timer interrupts only. In the
simulator however it works OK. So right now, you can only interrupt the
trial by pressing the TODO key. This does not work immediately, but
after a while. Switching off and on does not work. The Avigo just
resumes the computation. I will try to find a solution for this.

The main problem is the lack of a TestEvent function, which tests for events without waiting for them. There is a TestPutEvent
function but it only tests for events that you have put on the event
queue. It seems there is a special buffer for one such user put event.


I do switch off the auto power off during computation, and on again
after it is finished. So when the user interrupts the computation, its

auto power off setting may be switched off. I had not time to
investigate this.



Problems I met


I had two bugs, which were very hard to find. One was a missing case statement.

switch (TOWORD(x,y
{ cmdNewProblem :
ClearEvent(evType);
mix(); makeTarget();
PDESKBOX_draw(view);
break;
cmdNewTarget :
ClearEvent(evType);
makeTarget();
PDESKBOX_draw(view);
break;
}

A clever C compiler might issue a warning about two unused labels. But
this is one of the reasons, why Java abandoned simple form of goto and
label usage. Such errors tend to be very tough to detect.

The second bug was an index out of range. The simulated Avigo fell into
the setup routine (tab the screen here and so on), when the function in
question was called. This is not exactly a clear error message. Again,
such errors are much easier to find in Java.


René Grothmann

Kommentare

Noch keine Kommentare

Kommentar schreiben


Pavatar/Gravatar/Favatar/MyBlogLog Autoren Bilder werden unterstützt.
Die angegebene E-Mail-Adresse wird nicht dargestellt, sondern nur für eventuelle Benachrichtigungen verwendet.
Textile-Formatierung erlaubt
Letzte Kommentare
sprungmarker zu Ein Artikel mit MarsEdit
Mi, 19.11.2008 09:25
sprungmarkerZu Serendipity und Marsedit gibts […]


lr zu FoneLink 2.0 - drei Gratislizenzen zu vergeben
Di, 18.11.2008 20:48
lrAlso, falls noch eine Lizenz, wie […]


Walter zu Netlog
Di, 18.11.2008 08:58
WalterIch glaube, die “User-Demographie” […]


Robert Lender zu Netlog als Partnerbörse?
Mo, 17.11.2008 22:34
Robert LenderTut mir leid, ich habe keine Ahnung.


Termine
22.11.08 - 23.11.08 - BarCamp Graz (3 Tage)
26.11.08 - Blue Beanie Day 2008 (7 Tage)
29.11.08 - BarCamp Innsbruck (10 Tage)
15.12.08 - Web Montag Wien 14 (26 Tage)
13.06.09 - 14.06.09 - BarCamp Vienna (206 Tage)
XML
Creative Commons License
Dieser Inhalt ist unter einer Creative Commons-Lizenz lizenziert.