Notes {labels: 'NIL, viewFont: 10241} // nil=Unfiled, or specify a folder, e.g., 'Business. remove viewFont for current Styles default //ERASE! remove initial // to erase existing entries in this folder first life.nwt -- Slurpee format 1/1/96 Copyright 1994-96. S. Weyer. All Rights Reserved Worldwide. Newt source (verson 1.4) for "the game of Life" See life.txt & online help book for description (optional) life.bit for Bitmaps: LifeIcon (optional) LifeCnst.pkg plug-in for native methods ("arrN", "bitN") ---------- init func() begin // several constants :DefConst('kFirstRowCol,1000); :DefConst('kLastRowCol,-1); end ---------- Life //:doObj('add, 'Life) //:doObj('remove, 'Life) //:doObj('build, 'Life) {// the main Life application viewClass: clView, viewBounds: NIL, // computed in viewSetupFormScript viewFlags: 4, // vApplication viewFormat: 337, // vfFillWhite + vfFrameBlack + vfPen*1, declareSelf: 'base, // for close box title: "Life 1.4", // sizeX can be <= 30. others computed in viewSetupFormScript sizeX: 26, // initial width of universe. right bits of a longint to use [ <= 30] // value of sizeX should also be included in xSizes list (see gridPicker) // these are computed from sizeX at run-time in viewSetupFormScript sizeY: NIL, // height of universe. cellSize: NIL, // size of a cell. maximize for screen width (maxAppWidth DIV sizeX) vSizeX: NIL, // width of drawing view (cellSize*sizeX) [<= maxAppWidth] vSizeY: NIL, // height of drawing view offsetX: NIL, // number of bit columns on left to skip (sizeX-32 *cellSize) maxAppWidth: NIL, maxDrawHeight: NIL, firstRow: NIL, // to avoid dead rows above lastRow: NIL, // to avoid dead rows below firstCol: NIL, // ditto for columns lastCol: NIL, pattern: NIL, // defaults to dot. set by patternPicker nGen: 0, // number of generations (clear =0, nextGeneration +1) universe: NIL, // representation of cells (array or bitmap) curMethod: 0, // arr*,arrI,arrN,bit,bitI,bitN. set by methodPicker kDrawTop: 20, // extra space above drawArea (for title, gridPicker) kDrawBot: 35, // below (for patternPicker, genPicker, status) viewSetupFormScript: func() begin self.Life := self; // for run-time access (rather than :parent()) GetRoot().ExtrasDrawer:?close(); // get it out of the way. save a little heap local ht, ap:= GetAppParams(); // configure to use entire current MP screen size self.viewBounds := RelBounds( ap.AppAreaLeft,ap.AppAreaTop, maxAppWidth := ap.AppAreaWidth, ht := ap.AppAreaHeight); maxDrawHeight := ht - kDrawTop - kDrawBot; stopped := true; :setGrid(sizeX,nil); end, setGrid: func(x,redo) // intialize or resize drawing area begin universe := NIL; sizeX := x; cellSize := maxAppWidth DIV sizeX; sizeY := maxDrawHeight DIV cellSize; vSizeX := cellSize * sizeX; // <= maxAppWidth. if sizeX divided evenly, no border vSizeY := cellSize * sizeY; // <= maxDrawHeight offsetX := (sizeX-32) * cellSize; // left bits not shown :clear(); if redo then :RedoChildren(); end, clear: func() begin :setUniverse(:makeUniverse()); nGen := 0; firstRow := firstCol := kFirstRowCol; // all empty lastRow := lastCol := kLastRowCol; if drawArea exists then drawArea:?Dirty(); // redraw next idle via viewDrawScript end, makeUniverse: func() Array(sizeY, 0), // leave rows 0 until needed setUniverse: func(u) universe := u, _package: { icon: :GetPictAsBits("Bitmaps","LifeIcon"), // comment out if life.bit not transferred }, } ---------- Life.init1~ {// this gets added to app frame but a little easier for editing if it's separate helpBook: :helpBookTemplate(), // copy data structure from Newt at development-time // actual help pages are added later, i.e., Life.helpBook+page1, etc. helpView: NIL, viewQuitScript: func() begin if helpView then helpView:close(); helpView := universe := NIL; end, // to support Rotate for Newton OS 2.0 //reorienting: NIL, ReorientToScreen: func() begin //reorienting := TRUE; :SyncView(); :RedoChildren(); //reorienting := NIL; end, } ---------- Life.idleStuff~ {// some additional Life methods/slots related to idling ("Repeat") updateGen: TRUE, // to refresh gen counter during Repeat (default). set via genPicker nCount: NIL, // num of gen for timing tests. e.g., 40. set by genPicker viewIdleScript: func() if not stopped then begin if :nextGeneration() // generate then begin drawArea:drawDots(true); if updateGen then genPicker:updateText(NumberStr(nGen)); end; if firstRow < kFirstRowCol and (not nCount or nGen < nCount) then delayMSecs // reschedule else status.repeatButton:stop(nil); // empty universe. NIL end, delayMsecs: 5, // allow a few milliseconds for taps (e.g., Stop, Clear, Gen:) stopped: TRUE, } ---------- Life.arr~ {// these are added to app frame. also "copied" into _arr later // use an array for representation, draw dots explicitly //drawDots -- defined separately getCell: func(row,x) if row=0 // or x < 0 or x >= sizeX then 0 else row[x], getRow: func(univ,y) univ[y], getUniverse: func() universe, // makeUniverse -- defined earlier // nextGeneration -- defined separately setCell: func(row,x,b) begin if row=0 then row := Array(sizeX, 0); row[x] := if b then b else 1 - row[x]; row; end, setRow: func(univ,y,row) univ[y] := row, // setUniverse -- defined earlier } ---------- Life.drawDots func(idle) if idle then :dirty() // let viewDrawScript do it else if firstRow < kFirstRowCol then begin local drawMode := '{ // for ovals transferMode: 0, // modeCopy fillPattern: 5, // vfFillBlack }; local y,row,y1,y2,x,x1; for y := firstRow to lastRow do if isArray(row := universe[y]) // :getRow(universe,y) then begin y1 := y*cellSize; y2 := y1+cellSize; // constant for row for x := firstCol to lastCol do if row[x]>0 // :getCell(row,x) then begin x1 := (sizeX-x-1)*cellSize; :DrawShape(MakeOval(x1,y1,x1+cellSize,y2),drawMode); end; end; end ---------- Life.setCells func(row,x,vals,start,count) // generic version used by drawArea.viewGestureScript begin local vx, vlen := 0, fill := 0; if isArray(vals) then vlen := Length(vals) else fill := vals; for vx := start to start+count-1 do row := :setCell(row,x-vx,if vx < vlen then vals[vx] else fill); row; end ---------- Life.nextGeneration func() // generic method (used by "arr" & "bit") if firstRow < kFirstRowCol // only bother if there are some cells then begin local univ := :getUniverse(); // existing local newuniv := :makeUniverse(); // new, empty universe local x, y, neighbors, frx, lrx; local frow := firstRow-1, lrow := lastRow+1; if frow<0 then frow := 0; if lrow >= sizeY then lrow := sizeY-1; local fcol := firstCol-1, lcol := lastCol+1; if fcol<0 then fcol := 0; if lcol >= sizeX then lcol := sizeX-1; local newFirstRow := kFirstRowCol, newLastRow := kLastRowCol; local newFirstCol := kFirstRowCol, newLastCol := kLastRowCol; local n1, n2, n3; // in this layout visually. local n4, n5, n6; local n7, n8, n9; local row1 := 0 , row2 := 0, newRow := 0; local row3 := :getRow(univ,frow); // process from top to bottom, from right to left for y := frow to lrow // skip some dead rows before/after active area do begin row1 := row2; row2 := row3; row3 := if y0 or row2<>0 or row3<>0 then begin // this optimizes for empty row(s) frx := kFirstRowCol; lrx := kLastRowCol; n1 := :getCell(row1,fcol); n4 := :getCell(row2,fcol); n7 := :getCell(row3,fcol); newRow := n2 := n5 := n8 := n3 := n6 := n9 := 0; for x := fcol to lcol // skip some dead columns after/before active area do begin if row1<>0 then begin n3 := n2; n2 := n1; n1 := if x=lcol then 0 else :getCell(row1,x+1); neighbors := n1+n2+n3; end else neighbors := 0; if row2<>0 then begin n6 := n5; n5 := n4; n4 := if x=lcol then 0 else :getCell(row2,x+1); neighbors := neighbors + n4+n6; // omit center end; if row3<>0 then begin n9 := n8; n8 := n7; n7 := if x=lcol then 0 else :getCell(row3,x+1); neighbors := neighbors + n7+n8+n9; end; if neighbors=3 or (neighbors=2 and n5=1) then begin newRow := :setCell(newRow,x,1); if x < frx then frx := x; // 1st(min) lrx := x; end; end; if lrx > kLastRowCol // something set in row? then begin :setRow(newuniv,y,newRow); newLastRow := y; // last(max) if y < newFirstRow then newFirstRow := y; // 1st(min) if frx < newFirstCol then newFirstCol := frx; // min if lrx > newLastCol then newLastCol := lrx; // max end; end; end; firstRow := newFirstRow; lastRow := newLastRow; firstCol := newFirstCol; lastCol := newLastCol; :setUniverse(newuniv); nGen := nGen+1; // // refresh done via viewIdleScript or Next end ---------- Life+drawArea {// a drawing area (stepchild of Life) viewClass: clView, viewFlags: 2561, // vVisible + vClickable + vGesturesAllowed viewJustify: 16, // vjParentCenterH + vjParentTopV viewFormat: 337, // vfFillWhite + vfFrameBlack + vfPen*1, viewSetupFormScript: func() self.viewBounds := RelBounds(0,kDrawTop,vSizeX,vSizeY), viewDrawScript: func() begin // this is only invoked via :dirty (and it's stopped) :drawDots(nil); // (re)defined by arr/bit if stopped then begin // draw grid local x, y; local drawMode := '{ // for lines penSize: 1, penPattern: 3, // vfGray }; for y := cellSize to vSizeY-cellSize by cellSize // horizontal lines do :DrawShape(MakeLine(0,y,vSizeX,y), drawMode); for x := cellSize-1 to vSizeX-cellSize by cellSize // vertical lines do :DrawShape(MakeLine(x,0,x,vSizeY), drawMode); if updateGen and genPicker exists // might not during very initial build then genPicker:updateText(NumberStr(nGen)); end; end, } ---------- Life.drawArea.viewGestureScript func(unit, gestureID) begin local gb := :GlobalBox(), univ := :getUniverse(); local sb := StrokeBounds(unit); local ylen := if pattern then Length(pattern) else 0; // convert to local, grid coordinates. flip x local maxX := sizeX-1, maxY := sizeY-1; local x1 := maxX - max(0, (sb.left - gb.left) div cellSize); local y1 := max(0, (sb.top - gb.top) div cellSize); local x2 := maxX - min(maxX, (sb.right - gb.left) div cellSize); local y2 := min(maxY, (sb.bottom - gb.top) div cellSize); local xlen := x1-x2+1, y, fill := 0; if gestureID=13 or // aeScrub (gestureID=16 and fill:=1) // aeLine then for y := y1 to y2 // fill area do :setRow(univ,y, :setCells(:getRow(univ,y),x1, fill,0,xlen)) else if ylen>0 then begin // copy a pattern if y2+ylen > sizeY then ylen := sizeY - y2; // clip pattern at bottom y2 := y2 + ylen - 1; xlen := Length(pattern[0]); // note: uses length of first row as max width of pattern if x2-xlen < -1 then xlen := x2+1; // clip pattern at right x2 := x2 - xlen + 1; for y := 0 to ylen-1 do :setRow(univ, y+y1, :setCells(:getRow(univ,y+y1),x1, pattern[y],0,xlen)); end else // toggle single cell :setRow(univ,y1, :setCell(:getRow(univ,y1),x1,NIL)); // expand live area if y1 < firstRow then firstRow := y1; if y2 > lastRow then lastRow := y2; if x2 < firstCol then firstCol := x2; if x1 > lastCol then lastCol := x1; :Dirty(); // returns true end ---------- Life._arr {// this is the default version, so just copy the built-in versions drawDots: Life.drawDots, getCell: Life.getCell, getRow: Life.getRow, getUniverse: Life.getUniverse, makeUniverse: Life.makeUniverse, nextGeneration: Life.nextGeneration, setCell: Life.setCell, setRow: Life.setRow, setUniverse: Life.setUniverse, } ---------- Life._arrI {// same as _arr, but nextGeneration inlined drawDots: _arr.drawDots, getCell: _arr.getCell, getRow: _arr.getRow, getUniverse: _arr.getUniverse, makeUniverse: _arr.makeUniverse, nextGeneration: _arr.nextGeneration, // redefine (next) setCell: _arr.setCell, setRow: _arr.setRow, setUniverse: _arr.setUniverse, } ---------- Life._arrN {// same as _arr but nextGeneration native drawDots: _arr.drawDots, getCell: _arr.getCell, getRow: _arr.getRow, getUniverse: _arr.getUniverse, makeUniverse: _arr.makeUniverse, nextGeneration: if arrN_nextGeneration exists then arrN_nextGeneration else _arr.nextGeneration, setCell: _arr.setCell, setRow: _arr.setRow, setUniverse: _arr.setUniverse, } ---------- Life._arrI.nextGeneration func() // inlined version. ("arrN" adds INT,ARRAY declarations) if firstRow < kFirstRowCol then begin local univ := universe; local newuniv := Array(sizeY,0); local x, y, neighbors, frx, lrx; local frow := firstRow-1, lrow := lastRow+1; if frow<0 then frow := 0; if lrow >= sizeY then lrow := sizeY-1; local fcol := firstCol-1, lcol := lastCol+1; if fcol<0 then fcol := 0; if lcol >= sizeX then lcol := sizeX-1; local newFirstRow := kFirstRowCol, newLastRow := kLastRowCol; local newFirstCol := kFirstRowCol, newLastCol := kLastRowCol; local n1, n2, n3; local n4, n5, n6; local n7, n8, n9; local row1 := 0 , row2 := 0, newRow := 0; local row3 := univ[frow]; for y := frow to lrow do begin row1 := row2; row2 := row3; row3 := if y0 or row2<>0 or row3<>0 then begin frx := kFirstRowCol; lrx := kLastRowCol; n1 := if row1=0 then 0 else row1[fcol]; n4 := if row2=0 then 0 else row2[fcol]; n7 := if row3=0 then 0 else row3[fcol]; newRow := n2 := n5 := n8 := n3 := n6 := n9 := 0; for x := fcol to lcol do begin if row1<>0 then begin n3 := n2; n2 := n1; n1 := if x=lcol or row1=0 then 0 else row1[x+1]; neighbors := n1+n2+n3; end else neighbors := 0; if row2<>0 then begin n6 := n5; n5 := n4; n4 := if x=lcol or row2=0 then 0 else row2[x+1]; neighbors := neighbors + n4+n6; end; if row3<>0 then begin n9 := n8; n8 := n7; n7 := if x=lcol or row3=0 then 0 else row3[x+1]; neighbors := neighbors + n7+n8+n9; end; if neighbors=3 or (neighbors=2 and n5=1) then begin if newRow=0 then newRow := Array(sizeX, 0); newRow[x] := 1; if x < frx then frx := x; lrx := x; end; end; if lrx > kLastRowCol then begin newuniv[y] := newRow; newLastRow := y; if y < newFirstRow then newFirstRow := y; if frx < newFirstCol then newFirstCol := frx; if lrx > newLastCol then newLastCol := lrx; end; end; end; firstRow := newFirstRow; lastRow := newLastRow; firstCol := newFirstCol; lastCol := newLastCol; universe := newuniv; nGen := nGen+1; end ---------- Life._bit {// use a bitmap representation drawDots: func(idle) // this is sent to drawArea from viewIdleScript or viewDrawScript :copyBits(universe, offsetX, 0, nil), getCell: func(row,x) // return a bit: 0 or 1 // left shifting sign bit (x=29) fills 1's (so usually test first) if row=0 //or x >= sizeX then 0 else BAND(row >> x, 1), // 0 <= x < sizeX. 0 on right getRow: func(bits,y) // return a row (30-bit integer) in bitmap. 0<=y0 // 1, NIL then bit1 else if (if b then b=0 else BAND(row, bit1) <> 0) then BAND(row, BNOT(bit1)) // :=0 else BOR(row, bit1); // :=1 end, setRow: func(bits,y,row) // replace a row (30-bit integer) in bitmap StuffLong(bits, 16 + (y*4), row), setUniverse: func(u) // set current bitmap if isFrame(universe) then universe.bits := u else universe := { // a bitmap frame bounds: RelBounds(0, 0, 32*cellSize, vSizeY), bits: u, // reset this each generation }, } ---------- Life._bitI {// keep copies of refs drawDots: _bit.drawDots, getCell: _bit.getCell, getRow: _bit.getRow, getUniverse: _bit.getUniverse, makeUniverse: _bit.makeUniverse, nextGeneration: _bit.nextGeneration, // redefine (next) setCell: _bit.setCell, setRow: _bit.setRow, setUniverse: _bit.setUniverse, } ---------- Life._bitN {// same as bit but with two native methods from plug-in LifeConst:TKnollSys // nextGeneration and makeUniverse have some declarations and provide most of the benefit drawDots: _bit.drawDots, getCell: _bit.getCell, getRow: _bit.getRow, getUniverse: _bit.getUniverse, makeUniverse: if bitN_makeUniverse exists then bitN_makeUniverse else _bit.makeUniverse, nextGeneration: if bitN_nextGeneration exists then bitN_nextGeneration else _bit.nextGeneration, setCell: _bit.setCell, setRow: _bit.setRow, setUniverse: _bit.setUniverse, } ---------- Life._bitI.nextGeneration func() // inline version. ("bitN" adds INT declarations) if firstRow < kFirstRowCol then begin local univ := universe.bits; local newuniv := :makeUniverse(); // following locals are INT in native (bitN) version local x, y, neighbors, frx, lrx; local frow := firstRow-1, lrow := lastRow+1; if frow<0 then frow := 0; if lrow >= sizeY then lrow := sizeY-1; local fcol := firstCol-1, lcol := lastCol+1; if fcol<0 then fcol := 0; if lcol >= sizeX then lcol := sizeX-1; local newFirstRow := kFirstRowCol, newLastRow := kLastRowCol; local newFirstCol := kFirstRowCol, newLastCol := kLastRowCol; local n1, n2, n3; local n4, n5, n6; local n7, n8, n9; local row1 := 0 , row2 := 0, newRow := 0; local row3 := ExtractLong(univ, 16 + (frow*4)); for y := frow to lrow do begin row1 := row2; row2 := row3; row3 := if y0 or row2<>0 or row3<>0 then begin frx := kFirstRowCol; lrx := kLastRowCol; n1 := BAND(row1 >> fcol, 1); n4 := BAND(row2 >> fcol, 1); n7 := BAND(row3 >> fcol, 1); newRow := n2 := n5 := n8 := n3 := n6 := n9 := 0; for x := fcol to lcol do begin if row1<>0 then begin n3 := n2; n2 := n1; n1 := if x=lcol or row1=0 then 0 else BAND(row1 >> (x+1), 1); neighbors := n1+n2+n3; end else neighbors := 0; if row2<>0 then begin n6 := n5; n5 := n4; n4 := if x=lcol then 0 else BAND(row2 >> (x+1), 1); neighbors := neighbors + n4+n6; end; if row3<>0 then begin n9 := n8; n8 := n7; n7 := if x=lcol then 0 else BAND(row3 >> (x+1), 1); neighbors := neighbors + n7+n8+n9; end; if neighbors=3 or (neighbors=2 and n5=1) then begin newRow := BOR(newRow, 1 << x); if x < frx then frx := x; lrx := x; end; end; if lrx > kLastRowCol then begin StuffLong(newuniv, 16 + (y*4), newRow); newLastRow := y; if y < newFirstRow then newFirstRow := y; if frx < newFirstCol then newFirstCol := frx; if lrx > newLastCol then newLastCol := lrx; end; end; end; firstRow := newFirstRow; lastRow := newLastRow; firstCol := newFirstCol; lastCol := newLastCol; universe.bits := newuniv; nGen := nGen+1; end ---------- Life+titleObj {// title at top (displays Life.title) _proto: protoTitle, } ---------- Life+gridPicker {// a picker for choosing size of grid _proto: protoLabelPicker, text: "grid", viewBounds: RelBounds(1,1,100,14), xSizes: [30,26,24,20,16,12], // some convenient sizes. max 30. include original sizeX labelActionScript: func(i) Life:setGrid(xSizes[i],true), textSetup: func() // current size (sizeX had better be in list) labelCommands[SetContains(xSizes,sizeX)], viewSetupFormScript: func() begin local xsize; self.labelCommands := foreach xsize in xSizes // e.g., 30x35 collect xsize & "x" & maxDrawHeight DIV (maxAppWidth DIV xsize); inherited:?viewSetupFormScript(); end, } ---------- Life+methodPicker {// a picker for chooosing various representation/display methods _proto: protoLabelPicker, text: "method", viewBounds: RelBounds(-87,1,86,14), labelCommands: ["Arr", "ArrI", "ArrN", "Bit", "BitI", "BitN",], viewJustify: 32, // vjParentRightH labelActionScript: func(i) begin // splice in different versions of methods curMethod := i; local msg, meths := Life.(Intern("_" & labelCommands[i])); foreach msg in '[ drawDots, getCell, getRow, getUniverse, makeUniverse, nextGeneration, setCell, setRow, setUniverse] do Life.(msg) := meths.(msg); :clear(); end, textSetup: func() labelCommands[curMethod], } ---------- Life+patternPicker {// a picker for pre-built patterns _proto: protoLabelPicker, viewBounds: RelBounds(1, -33, 135, 14), text: "pattern", viewJustify: 128 + 8388608, // vjParentBottomV + vjLeftH + oneLineOnly, patterns: [// order matches names in labelCommands NIL, // none=dot NIL, // menu pickseparator line -------- [[0,1,0,], // glider (note: 1st line of pattern should be max width (pad right with 0s if nec) [1,], // subsequent rows can omit trailing 0s [1,1,1,]], [[1,1,1,]], // blinker [[1,1], // block [1,1]], [[0,1,1,0,], // beehive [1,0,0,1,], [0,1,1,]], ], labelCommands: ["", 'pickSeparator, "glider", "blinker", "block", "beehive",], labelActionScript: func(i) pattern := patterns[i], // for copying in drawArea.viewGestureScript textSetup: func() // name of current pattern labelCommands[SetContains(patterns,pattern)], } ---------- Life+genPicker {// a "generation" count picker _proto: protoLabelPicker, text: "gen", viewBounds: RelBounds(-105,-33,104,14), // at right, just above status bar labelCommands: [ "0", // update current gen# (nGen) "<0>", // don't update "", // don't update. do timing&stop at 40 "", // don't udpate. do timing&stop at 100 ], viewJustify: 32+128,// vjParentRightH+vjParentBottomV labelActionScript: func(i) begin updateGen := i=0; nCount := if i = 2 then 40 else if i=3 then 100 else NIL; nGen := 0; end, textSetup: func() // name of current gen option labelCommands[ if ncount=40 then 2 else if ncount=100 then 3 else if updateGen then 0 else 1], } ---------- ButtonBounds func(width) // used for adding embedded buttons to status // this is defined as a Newt development-time method // once the buttons are initialized and added to status, // it really isn't needed anymore (and it isn't saved with the package) // also not needed if ViewCnst.pkg plug-in is used begin local left1:=25, top:=2, spacing:=6, bottom:=15; if width > 0 then SetBounds(spacing, top, spacing + width, bottom) // other buttons else SetBounds(left1, top, left1 - width, bottom); // first button end ---------- Life+status {// status bar with clock and closebox _proto: protoStatus, } ---------- Life.status+clearButton {_proto: protoTextButton, viewBounds: :ButtonBounds(-40), // first button (stepchild) in status area text: "Clear", buttonClickScript: func() Life:Delete('clear,[]), // built-in crumple/trash can effect } ---------- Life.status+nextButton {_proto: protoTextButton, viewBounds: :ButtonBounds(40), // second button viewJustify: 8389638, // vjParentLeftH + vjParentTopV + vjSiblingRightH + oneLineOnly + vjCenterH + vjCenterV, text: "Next", buttonClickScript: func() if stopped then begin :nextGeneration(); drawArea:dirty(); end, } ---------- Life.status+repeatButton {_proto: protoTextButton, viewBounds: :ButtonBounds(40), // third button viewJustify: 8389638, text: "Repeat", buttonClickScript: func() if firstRow < kFirstRowCol // not empty? then if stopped then begin // start up background process gc(); time1 := Ticks(); stopped := Life:setupIdle(delayMsecs); // start background process. NIL SetValue(self, 'text, "Stop"); end else :stop(true), stop: func(fl) begin // used to stop things from repeatButton.buttonClickScript or viewIdleScript if fl then Life:SetupIdle(0); // halt background process if nCount then begin Print("time:" && Ticks() - time1); nGen := 0; end; stopped := TRUE; SetValue(self, 'text, "Repeat"); drawArea:dirty(); // redraw with grid NIL; // turn off idling by returning nil end, time1: NIL, // for possible timing (if nCount non-nil) } ---------- Life.status+zhelpButton {_proto: protoTextButton, viewBounds: :ButtonBounds(40), // fourth button (zhelp sorts 4th) viewJustify: 8389638, text: "Info", buttonClickScript: func() if helpBook and stopped // open help book then begin local TT := GetRoot().TinyTim; // the help reader app if not helpView then helpView := BuildContext( {_proto: TT._proto, bookRef: helpBook}); TT:Close(); // in case system help is currently open helpView:openManual(helpBook); end, } ---------- Life.helpBook+page1 .# first help book page. this is much easier than dealing with BookMaker! .subject 1 Describe Life .story Life is a mathematical simulation game described by John Conway in Scientific American in Oct. 1970. Several rules are used to create a next generation from a cell's 8 neighbors: - Survival: A cell with 2 or 3 neighbors survives. - Death: A cell with 1 or less, or 4 or more neighbors, "dies". - Birth: An empty cell with exactly 3 neighbors will be "born". ---------- Life.helpBook+page2 .subject 1 Understand the Code .story This version of Life (1.4) has six variations/optimizations, which are explained in the file: life.txt. The article "Life with NewtonScript" by David Betz in Byte, March 1994, pp. 191-194 provides some introduction/background. You could also register for the Newt Development Environment (see Develop) to obtain a Newton programming manual and more NewtonScript application examples. ---------- Life.helpBook+page3 .subject 1 Distribute .story This version of Life is freeware and may be distributed freely as long as all of the files are included and unmodified. You are free to make modifications for your own use. Copyright 1994-96, S. Weyer. All Rights Reserved Worldwide. ---------- Life.helpBook+page4 .subject 1 Use .story \uFC01\u grid: grid size \uFC01\u method: computation method \uFC01\u pattern: fill pattern for \uFC01\u gen: turn on/off counter,timing : add a pattern, or toggle a cell : fill an area : erase an area [Clear]: clear all cells, reset Gen. counter [Next]: evolve a single generation [Repeat]: evolve until all cells are empty or you tap Stop or Clear [Info]: this help book ---------- Life.helpBook+page5 .subject 1 Develop .story If you are interested in object-oriented programming, developing applications in NewtonScript and saving as packages directly on your Newton, try the "Newt" development environment. Registered users receive a 80+ pp. manual (Acrobat or paper) and 190+ examples. For further info, see life.txt or http://www.netaxs.com/~weyer/newton/releases.html ---------- Life.helpBook+page6 .subject 1 Contact Author .story Steve Weyer 17 Timber Knoll Drive Washington Crossing, PA 18977-1052 Internet: weyer@netaxs.com America Online, eWorld, NewtonMail: SteveWeyer Compuserve: 74603,2051 http://www.netaxs.com/~weyer/newton/releases.html ---------- BYE!