-- MathDrill.mesa
-- Edited by Sweet, May 14, 1981 11:46 PM
DIRECTORY
Ascii,
Inline,
IODefs,
PressDefs,
PressUtilities,
Random,
String;
MathDrill: PROGRAM
IMPORTS Inline, IODefs, PressDefs, PressUtilities, Random, String =
BEGIN OPEN PressDefs;
pfdBody: PressFileDescriptor;
pfd: POINTER TO PressFileDescriptor = @pfdBody;
Mica: TYPE = CARDINAL;
MBox: TYPE = RECORD [x,y,w,h: Mica];
LineWidth: Mica ← 30;
CharHeight: Mica;
CharWidth: POINTER TO ARRAY CHARACTER OF Mica;
MathCharWidth: ARRAY CHARACTER OF Mica;
TextCharWidth: ARRAY CHARACTER OF Mica;
AnswerCharWidth: ARRAY CHARACTER OF Mica;
MathCharHeight: Mica;
TextCharHeight: Mica;
AnswerCharHeight: Mica;
Minus: CHARACTER = Ascii.ControlX;
answers: BOOLEAN ← FALSE;
PointsToMicas: PROC [points: CARDINAL] RETURNS [Mica] =
{RETURN [Inline.LongDiv[Inline.LongMult[points, MicasPerInch],72]]};
maxSum: CARDINAL ← 5;
minVal: CARDINAL ← 1;
plusNum: CARDINAL ← 2;
plusDen: CARDINAL ← 3;
Prob: TYPE = RECORD [a,b: [0..16), op: CHARACTER];
HistSize: CARDINAL = 6;
history: ARRAY [0..HistSize) OF Prob ← ALL[[0,0,0C]];
hp: CARDINAL ← 0;
Problem: PROC RETURNS [a,b: CARDINAL, op: CHARACTER] =
BEGIN
trial: Prob;
DO
trial ← TrialProblem[];
FOR i: CARDINAL IN [0..HistSize) DO
IF history[i] = trial THEN EXIT;
REPEAT
FINISHED => GO TO ok;
ENDLOOP;
REPEAT
ok => NULL;
ENDLOOP;
history[hp] ← trial; hp ← (hp+1) MOD HistSize;
RETURN [trial.a, trial.b, trial.op];
END;
NonZeroTrialProblem: PROC RETURNS [trial: Prob] =
BEGIN
trial.op ← IF Random.InRange[1, plusDen] <= plusNum THEN '+ ELSE Minus;
trial.a ← Random.InRange[2 * minVal,maxSum];
trial.b ← Random.InRange[minVal,trial.a-minVal];
IF trial.op = '+ THEN trial.a ← trial.a-trial.b;
END;
TrialProblem: PROC RETURNS [trial: Prob] =
BEGIN
IF minVal = 0 THEN RETURN[ZeroTrialProblem[]]
ELSE RETURN [NonZeroTrialProblem[]];
END;
w1: CARDINAL ← 2;
w2: CARDINAL ← 4;
w3: CARDINAL ← 3;
bigNum: CARDINAL ← 2;
bigDen: CARDINAL ← 3;
minBig: CARDINAL ← 5;
ZeroTrialProblem: PROC RETURNS [trial: Prob] =
BEGIN
total, a, b: CARDINAL;
trial.op ← IF Random.InRange[1, plusDen] <= plusNum THEN '+ ELSE Minus;
total ← IF Random.InRange[1, bigDen] <= bigNum THEN Random.InRange[minBig, maxSum]
ELSE Random.InRange[1,maxSum];
a ← IF total = 0 OR Random.InRange[1, w2*total] <= w3 THEN 0
ELSE Random.InRange[1, total-1];
b ← total - a;
IF Random.InRange[1,2] = 1 THEN {t: CARDINAL = a; a ← b; b ← t};
IF trial.op = '+ THEN {trial.a ← a; trial.b ← b}
ELSE {trial.a ← total; trial.b ← b};
END;
StringWidth: PROC [s: STRING] RETURNS [l: Mica] =
BEGIN
l ← 0;
FOR i: CARDINAL IN [0..s.length) DO
l ← l + CharWidth[s[i]];
ENDLOOP;
END;
LineY: PROC [box: POINTER TO MBox, line, of: CARDINAL, lead: Mica ← 0]
RETURNS [Mica] =
BEGIN
h: Mica = CharHeight;
bottom: Mica = (box.h- of*h - (of-1)*lead)/2;
RETURN [box.y + bottom + (line-1)*(h+lead)];
END;
CenterLine: PROC [s: STRING, box: POINTER TO MBox, line, of: CARDINAL, lead: Mica ← 0] =
BEGIN
w: Mica = StringWidth[s];
y: Mica = LineY[box: box, line: line, of: of, lead: lead];
x: Mica = box.x + (box.w - w)/2;
PutText[pfd, s, x, y];
END;
RJLine: PROC [s: STRING, box: POINTER TO MBox, line, of: CARDINAL, lead: Mica ← 0] =
BEGIN
w: Mica = StringWidth[s];
y: Mica = LineY[box: box, line: line, of: of, lead: lead];
x: Mica = box.x + (box.w - w);
PutText[pfd, s, x, y];
END;
LJLine: PROC [s: STRING, box: POINTER TO MBox, line, of: CARDINAL, lead: Mica ← 0] =
BEGIN
y: Mica = LineY[box: box, line: line, of: of, lead: lead];
PutText[pfd, s, box.x, y];
END;
DomLine: PROC [c: CHARACTER, n: CARDINAL, box: POINTER TO MBox, y: Mica] =
BEGIN
s: STRING ← [1];
cw: Mica = CharWidth[c];
w: Mica = n*cw + (n-1)*InterCharWidth;
x: Mica ← box.x + (box.w - w)/2;
s.length ← 1; s[0] ← c;
THROUGH [0..n) DO
PutText[pfd, s, x, y];
x ← x + cw + InterCharWidth;
ENDLOOP;
END;
DrawBorder: PROC [box: POINTER TO MBox] =
BEGIN
PutRectangle[p: pfd,
xstart: box.x, ystart: box.y, xlen: box.w + LineWidth, ylen: LineWidth];
PutRectangle[p: pfd,
xstart: box.x, ystart: box.y, xlen: LineWidth, ylen: box.h];
PutRectangle[p: pfd,
xstart: box.x + box.w, ystart: box.y, xlen: LineWidth, ylen: box.h];
PutRectangle[p: pfd,
xstart: box.x, ystart: box.y + box.h,
xlen: box.w + LineWidth, ylen: LineWidth];
END;
Mbw: Mica ← (3*MicasPerInch)/4;
Mbh: Mica ← (3*MicasPerInch)/4;
Mbs: Mica ← MicasPerInch/4;
Mch: Mica ← (3*MicasPerInch)/8;
MPb: Mica ← PointsToMicas[2];
InterCharWidth: Mica ← PointsToMicas[3];
Pattern: TYPE = RECORD [lines: CARDINAL, line: ARRAY [1..5] OF CARDINAL];
pat: ARRAY [0..10] OF Pattern ← [
[0,],
[1,[1,,,,]],
[1,[2,,,,]],
[2,[2,1,,,]],
[2,[2,2,,,]],
[2,[3,2,,,]],
[3,[3,2,1,,]],
[3,[3,2,2,,]],
[3,[3,3,2,,]],
[3,[4,3,2,,]],
[4,[4,3,2,1,]]];
Domino: PROC [c: CHARACTER, n: CARDINAL, box: POINTER TO MBox] =
BEGIN
p: Pattern = pat[n];
DrawBorder[box];
MathFont[];
FOR i: CARDINAL IN [1..p.lines] DO
DomLine[
c: c,
n: p.line[i],
box: box,
y: LineY[box: box, line: i, of: p.lines, lead: MPb]];
ENDLOOP;
END;
DomChar: ARRAY [0..4) OF CHARACTER ← ['1, '2, '8, '0];
GraphicNumberSentence: PROC [x,y: Mica] RETURNS [Mica] =
BEGIN
ns: STRING ← [2];
box: MBox;
a,b: CARDINAL;
op: CHARACTER;
dc: CHARACTER ← DomChar[Random.InRange[0,3]];
yDom: Mica = y - Mbh;
yText: Mica = yDom - Mch;
[a, b, op] ← Problem[];
box ← [x: x, y: yDom, w: Mbw, h: Mbh];
Domino[dc, a, @box];
box.x ← box.x + Mbw + Mbs;
Domino[dc, b, @box];
TextFont[];
box ← [x: x, y: yText, w: Mbw, h: Mch];
ns.length ← 0;
String.AppendDecimal[ns, a];
CenterLine[s: ns, box: @box, line: 1, of: 1];
box ← [x: x + Mbw, y: yText, w: Mbs, h: Mch];
ns.length ← 1;
ns[0] ← op;
CenterLine[s: ns, box: @box, line: 1, of: 1];
ns.length ← 0;
String.AppendDecimal[ns, b];
box ← [x: x + Mbw + Mbs, y: yText, w: Mbw, h: Mch];
CenterLine[s: ns, box: @box, line: 1, of: 1];
box ← [x: x + 2*Mbw + Mbs, y: yText, w: Mbs, h: Mch];
CenterLine[s: "="L, box: @box, line: 1, of: 1];
PutRectangle[p: pfd,
xstart: x + 2*Mbw + 2*Mbs,
ystart: yText,
xlen: M12,
ylen: P1];
IF answers THEN
BEGIN
c: CARDINAL ← IF op = '+ THEN a+b ELSE a-b;
ns.length ← 0;
AnswerFont[];
String.AppendDecimal[ns, c];
box.x ← box.x + Mbs; box.w ← M12;
CenterLine[s: ns, box: @box, line: 1, of: 1];
TextFont[];
END;
RETURN[y - (3*MicasPerInch)/2];
END;
P1: Mica = PointsToMicas[1];
P2: Mica = PointsToMicas[2];
M12: Mica = MicasPerInch/2;
M14: Mica = MicasPerInch/4;
M34: Mica = (3*MicasPerInch)/4;
M38: Mica = (3*MicasPerInch)/8;
VertNumberSentence: PROC [x,y: Mica] RETURNS [Mica] =
BEGIN
box: MBox;
a,b: CARDINAL;
op: CHARACTER;
ns: STRING ← [3];
[a, b, op] ← Problem[];
box ← [x: x, y: y-M12+CharHeight + 2*P2, w: M12, h: CharHeight];
ns.length ← 0;
String.AppendDecimal[ns, a];
RJLine[s: ns, box: @box, line: 1, of: 1];
box.y ← box.y - CharHeight - P2;
ns.length ← 0;
String.AppendChar[ns, op];
String.AppendDecimal[ns, b];
RJLine[s: ns, box: @box, line: 1, of: 1];
PutRectangle[p: pfd,
xstart: x,
ystart: y - M12 - P2,
xlen: M12,
ylen: P2];
IF answers THEN
BEGIN
c: CARDINAL ← IF op = '+ THEN a+b ELSE a-b;
ns.length ← 0;
AnswerFont[];
String.AppendDecimal[ns, c];
box.y ← y - M12 - 3*P2 - CharHeight;
RJLine[s: ns, box: @box, line: 1, of: 1];
TextFont[];
END;
RETURN [y - MicasPerInch];
END;
CenterNum: PROC [n: CARDINAL, box: POINTER TO MBox] =
BEGIN
w: Mica;
ns: STRING ← [2];
String.AppendDecimal[ns, n];
w ← StringWidth[ns];
PutText[pfd, ns, box.x + (box.w-w)/2, box.y + (box.h-CharHeight)/2];
END;
CenterChar: PROC [c: CHARACTER, box: POINTER TO MBox] =
BEGIN
w: Mica;
ns: STRING ← [2];
ns.length ← 1; ns[0] ← c;
w ← StringWidth[ns];
PutText[pfd, ns, box.x + (box.w-w)/2, box.y + (box.h-CharHeight)/2];
END;
HorizNumberSentence: PROC [x,y: Mica] RETURNS [Mica] =
BEGIN
box: MBox;
a,b: CARDINAL;
op: CHARACTER;
ns: STRING ← [3];
[a, b, op] ← Problem[];
box ← [x: x, y: y-M38, w: M38, h: M38];
CenterNum[a, @box];
box.x ← box.x + M38; box.w ← M14;
CenterChar[op, @box];
box.x ← box.x + M14; box.w ← M38;
CenterNum[b, @box];
box.x ← box.x + M38; box.w ← M14;
CenterChar['=, @box];
PutRectangle[p: pfd,
xstart: box.x + M14,
ystart: y-M38,
xlen: M12,
ylen: P1];
IF answers THEN
BEGIN
c: CARDINAL ← IF op = '+ THEN a+b ELSE a-b;
AnswerFont[];
box.x ← box.x + M14; box.w ← M12;
CenterNum[c, @box];
TextFont[];
END;
RETURN[y - M12];
END;
TextFont: PROC =
BEGIN
SetFont[p: pfd, Name: "Helvetica", PointSize: 18, Face: 2];
CharHeight ← TextCharHeight;
CharWidth ← @TextCharWidth;
END;
AnswerFont: PROC =
BEGIN
SetFont[p: pfd, Name: "Helvetica", PointSize: 14, Face: 1];
CharHeight ← AnswerCharHeight;
CharWidth ← @AnswerCharWidth;
END;
MathFont: PROC =
BEGIN
SetFont[p: pfd, Name: "Math", PointSize: 10, Face: 0];
CharHeight ← MathCharHeight;
CharWidth ← @MathCharWidth;
END;
DigestFonts: PROC =
BEGIN
[] ← PressUtilities.FindFontWidths[
family: "Helvetica"L,
points: 18,
weight: bold,
slope: regular,
widths: LOOPHOLE[@TextCharWidth]];
TextCharHeight ← PointsToMicas[18];
[] ← PressUtilities.FindFontWidths[
family: "Helvetica"L,
points: 14,
weight: medium,
slope: italic,
widths: LOOPHOLE[@AnswerCharWidth]];
AnswerCharHeight ← PointsToMicas[14];
[] ← PressUtilities.FindFontWidths[
family: "Math"L,
points: 10,
weight: medium,
slope: regular,
widths: LOOPHOLE[@MathCharWidth]];
MathCharHeight ← PointsToMicas[10];
END;
FirstDoPage: PROC =
BEGIN
x, y: Mica;
x ← MicasPerInch;
y ← 10*MicasPerInch;
THROUGH [0..6) DO y ← GraphicNumberSentence[x, y]; ENDLOOP;
x ← 4*MicasPerInch;
y ← 10*MicasPerInch;
PutRectangle[p: pfd,
xstart: x - M14,
ystart: MicasPerInch,
xlen: P1,
ylen: 9*MicasPerInch];
THROUGH [0..9) DO
[] ← VertNumberSentence[x, y];
y ← VertNumberSentence[x + MicasPerInch, y];
ENDLOOP;
x ← x + 2*MicasPerInch;
y ← 10*MicasPerInch;
PutRectangle[p: pfd,
xstart: x - M14,
ystart: MicasPerInch,
xlen: P1,
ylen: 9*MicasPerInch];
THROUGH [0..18) DO y ← HorizNumberSentence[x, y]; ENDLOOP;
END;
DoPage: PROC =
BEGIN
x, y: Mica;
AnswerFont[];
TextFont[]; -- to make sure font is correctly set
x ← MicasPerInch;
y ← 10*MicasPerInch;
THROUGH [0..9) DO
[] ← VertNumberSentence[x, y];
[] ← VertNumberSentence[x + MicasPerInch, y];
y ← VertNumberSentence[x + 2*MicasPerInch, y];
ENDLOOP;
x ← x+ 3*MicasPerInch;
y ← 10*MicasPerInch;
PutRectangle[p: pfd,
xstart: x - (2*M14)/3,
ystart: MicasPerInch,
xlen: P1,
ylen: 9*MicasPerInch];
THROUGH [0..18) DO
[] ← HorizNumberSentence[x, y];
y ← HorizNumberSentence[x + 2*MicasPerInch, y];
ENDLOOP;
END;
pageCount: CARDINAL ← 3;
Driver: PROC =
BEGIN
state: Random.State;
BEGIN OPEN IODefs;
WriteString["min: "L];
minVal ← ReadNumber[minVal, 10];
WriteChar[CR];
WriteString["max: "L];
maxSum ← ReadNumber[maxSum, 10];
WriteString["pages: "L];
pageCount ← ReadNumber[pageCount, 10];
END;
DigestFonts[];
InitPressFileDescriptor[pfd, "Math.drill"L];
answers ← FALSE;
Random.ReadState[@state];
THROUGH [0..pageCount) DO DoPage[]; WritePage[pfd]; ENDLOOP;
ClosePressFile[pfd];
Random.WriteState[@state];
history ← ALL[[0,0,0C]];
InitPressFileDescriptor[pfd, "Math.key"L];
answers ← TRUE;
THROUGH [0..pageCount) DO DoPage[]; WritePage[pfd]; ENDLOOP;
ClosePressFile[pfd];
END;
Driver[];
END.