-- 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.