Walnut/Ordinary Programming/Ordinary Computing Examples
From Erights
(Difference between revisions)
(FIELD_OTHER) |
Kevin Reid (Talk | contribs) m (Reverted edits by 89.19.172.22 (Talk); changed back to last version by Kevin Reid) |
||
Line 1: | Line 1: | ||
[[Category:Walnut|2]] | [[Category:Walnut|2]] | ||
- | + | ===Ordinary Computing Examples=== | |
+ | |||
+ | ====Pretty E==== | ||
+ | |||
+ | ====Racetrack Game for a single computer==== | ||
+ | |||
+ | Below is a simple game of racetrack: 3 cars are set on the track, to race between walls around curves to the finish line. Each driver can choose to accelerate or decelerate his car by +/- 1 space/turn during each turn. Do not build up too much velocity, you won't be able to slow down! | ||
+ | |||
+ | The example has just about everything in it: JPanels, objects, functions, Java API calls, it's all there. We will come back to this example later in the book, to make the track distributed and secure. Then we shall invite Satan for a little competition, with souls on the line. | ||
+ | |||
+ | <pre> | ||
+ | |||
+ | <nowiki># E sample | ||
+ | |||
+ | #!/usr/bin/env rune | ||
+ | |||
+ | # Copyright 2002 Combex, Inc. under the terms of the MIT X license | ||
+ | # found at http://www.opensource.org/licenses/mit-license.html | ||
+ | pragma.syntax("0.9") | ||
+ | |||
+ | def traceln(text) { stderr.println(text) } | ||
+ | |||
+ | def attachAction(component,target,verb) { | ||
+ | def listener { | ||
+ | to actionPerformed(event) { | ||
+ | try { | ||
+ | E.call(target, verb, []) | ||
+ | } catch problem { | ||
+ | throw <- (problem) # send to E tracelog instead of AWT | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | component.addActionListener(listener) | ||
+ | } | ||
+ | |||
+ | def newButton(labelText, verb, target) { | ||
+ | def button := <swing:makeJButton>(labelText) | ||
+ | button.setBackground(<awt:makeSystemColor>.getControl()) | ||
+ | attachAction(button,target,verb) | ||
+ | return button | ||
+ | } | ||
+ | |||
+ | def abs(number) {return if (number >= 0) {number} else {-number}} | ||
+ | |||
+ | def makeCoord(x,y) { | ||
+ | def coord { | ||
+ | to getX() {return x} | ||
+ | to getY() {return y} | ||
+ | to printOn(writer) {writer.print(`coord: $x,$y`)} | ||
+ | to samePlace(coord2) :boolean { | ||
+ | return x == coord2.getX() && y == coord2.getY() | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The "add" method is the underlying function for the "+" operator. | ||
+ | * Here, by writing an "add" method, we make coordinates work with "+" | ||
+ | */ | ||
+ | to add(coord2) {return makeCoord(x + coord2.getX(),y + coord2.getY())} | ||
+ | |||
+ | /** | ||
+ | * The "subtract" method is the underlying function for the "-" operator | ||
+ | */ | ||
+ | to subtract(coord2) {return makeCoord(x - coord2.getX(),y - coord2.getY())} | ||
+ | } | ||
+ | return coord | ||
+ | } | ||
+ | |||
+ | def makeInstrumentPanel(car) :near { | ||
+ | def makeIndicator(speed,positiveText,negativeText):pbc { | ||
+ | var answer := "" | ||
+ | var direction := positiveText | ||
+ | if (speed < 0) {direction := negativeText} | ||
+ | for i in 1..abs(speed) {answer := answer + direction} | ||
+ | if (speed == 0) {answer := "0"} | ||
+ | return answer | ||
+ | } | ||
+ | def makeXIndicator(speed) {return makeIndicator(speed,">","<")} | ||
+ | def makeYIndicator(speed) {return makeIndicator(speed,"^\n","V\n")} | ||
+ | def frame := <swing:makeJFrame>(`Car ${car.getName()} Instrument Panel`) | ||
+ | def lbl(text) {return <swing:makeJLabel>(text)} | ||
+ | def xLabel := lbl("Horizontal Speed:") | ||
+ | def xIndicator := <swing:makeJTextArea>() | ||
+ | xIndicator.setText("0") | ||
+ | def yLabel := <swing:makeJTextArea>("V \ne\nr\nt\ni\nc\na\nl") | ||
+ | yLabel.setBackground(<awt:makeSystemColor>.getControl()) | ||
+ | def yIndicator := <swing:makeJTextArea>() | ||
+ | yIndicator.setText("0") | ||
+ | def statusPane := lbl("Running...") | ||
+ | def instrumentPanel | ||
+ | def btn(name,action) {return newButton(name,action,instrumentPanel)} | ||
+ | def submitButton := btn("Submit","submit") | ||
+ | var acceleration := makeCoord(0,0) | ||
+ | def realPane :=JPanel` | ||
+ | ${lbl("")} $xLabel > > > | ||
+ | V $xIndicator > > > | ||
+ | $yLabel.Y $yIndicator ${btn("\\","upLeft")} ${btn("^","up")} ${btn("/","upRight")} | ||
+ | V V ${btn("<","left")} ${btn("0","zero")} ${btn(">","right")} | ||
+ | V V ${btn("/","downLeft")} ${btn("V","down")} ${btn("\\","downRight")} | ||
+ | V V $submitButton.X > > | ||
+ | $statusPane > > > >` | ||
+ | frame.setDefaultCloseOperation(<swing:makeWindowConstants>.getDO_NOTHING_ON_CLOSE()) | ||
+ | frame.getContentPane().add(realPane) | ||
+ | frame.pack() | ||
+ | frame.show() | ||
+ | bind instrumentPanel { | ||
+ | to submit() { | ||
+ | submitButton.setEnabled(false) | ||
+ | car.accelerate(acceleration) | ||
+ | } | ||
+ | to prepareForNextTurn() { | ||
+ | xIndicator.setText(makeXIndicator(car.getVelocity().getX())) | ||
+ | yIndicator.setText(makeYIndicator(-(car.getVelocity().getY()))) | ||
+ | acceleration := makeCoord(0,0) | ||
+ | submitButton.setEnabled(true) | ||
+ | # Note, public static transferFocus on awt Component is not Java API, added in E environment | ||
+ | <awt:makeComponent>.transferFocus([frame.getContentPane()], statusPane) | ||
+ | } | ||
+ | to setStatus(status) {statusPane.setText(status)} | ||
+ | to upLeft() {acceleration := makeCoord(-1,-1)} | ||
+ | to up() {acceleration := makeCoord(0,-1)} | ||
+ | to upRight() {acceleration := makeCoord(1,-1)} | ||
+ | to left() {acceleration := makeCoord(-1,0)} | ||
+ | to zero() {acceleration := makeCoord(0,0)} | ||
+ | to right() {acceleration := makeCoord(1,0)} | ||
+ | to downLeft() {acceleration := makeCoord(-1,1)} | ||
+ | to down() {acceleration := makeCoord(0,1)} | ||
+ | to downRight() {acceleration := makeCoord(1,1)} | ||
+ | } | ||
+ | return instrumentPanel | ||
+ | } | ||
+ | |||
+ | def makeCar(name,startLocation,raceMap) { | ||
+ | var location := startLocation | ||
+ | var acceleration := makeCoord(0,0) | ||
+ | var velocity := makeCoord(0,0) | ||
+ | var hasCrashed := false | ||
+ | var hasFinished := false | ||
+ | def instrumentPanel | ||
+ | def sign(x) { | ||
+ | return if (x > 0) { | ||
+ | 1 | ||
+ | } else if (x < 0) { | ||
+ | -1 | ||
+ | } else {0} | ||
+ | } | ||
+ | |||
+ | def accelReactors := [].asMap().diverge() | ||
+ | |||
+ | /** | ||
+ | * Compute the path the car will take from the location at the | ||
+ | * beginning of this turn to the end; return the result | ||
+ | * as a list of coords | ||
+ | */ | ||
+ | def computeIntermediateLocations(start,finish) { | ||
+ | def locations := [].diverge() | ||
+ | def slope := (finish.getY() - start.getY()) / (finish.getX() - start.getX()) | ||
+ | def computeRemainingLocations(current) { | ||
+ | var nextX := current.getX() | ||
+ | var nextY := current.getY() | ||
+ | var distToGo := 0 | ||
+ | #if the car is traveling faster in the x direction than | ||
+ | #in the y direction, increment x position by one along | ||
+ | #the path and compute the new y | ||
+ | if (slope < 1.0 && slope > -1.0) { | ||
+ | distToGo := finish.getX() - current.getX() | ||
+ | nextX += sign(distToGo) | ||
+ | def distTraveled := nextX - start.getX() | ||
+ | nextY := start.getY() + ((slope * distTraveled) + 0.5) //1 | ||
+ | #if the car is traveling faster in the y direction than | ||
+ | #in the x direction, increment y position by one along | ||
+ | #the path and compute new x | ||
+ | } else { | ||
+ | distToGo := finish.getY() - current.getY() | ||
+ | nextY += sign(distToGo) | ||
+ | def distTraveled := nextY - start.getY() | ||
+ | nextX := start.getX() + ((distTraveled/slope) + 0.5) //1 | ||
+ | } | ||
+ | def next := makeCoord(nextX,nextY) | ||
+ | locations.push(next) | ||
+ | if (!(next.samePlace(finish))) { | ||
+ | computeRemainingLocations(next) | ||
+ | } | ||
+ | } | ||
+ | computeRemainingLocations(start) | ||
+ | return locations | ||
+ | } | ||
+ | def car { | ||
+ | to accelerate(accel) { | ||
+ | traceln(`accelerating car $name`) | ||
+ | acceleration := accel | ||
+ | for each in accelReactors { | ||
+ | each.reactToAccel(car) | ||
+ | } | ||
+ | } | ||
+ | to move(){ | ||
+ | traceln("into move") | ||
+ | velocity += acceleration | ||
+ | def newLocation := location + velocity | ||
+ | traceln("got newlocation") | ||
+ | def path := computeIntermediateLocations(location,newLocation) | ||
+ | location := newLocation | ||
+ | traceln("assigned location") | ||
+ | hasCrashed := hasCrashed || raceMap.causesCrash(path) | ||
+ | hasFinished := hasFinished || raceMap.causesFinish(path) | ||
+ | traceln("got crash finish") | ||
+ | if (hasCrashed) { | ||
+ | instrumentPanel.setStatus("Crashed") | ||
+ | } else if (hasFinished) {instrumentPanel.setStatus("Finished")} | ||
+ | traceln("out of move") | ||
+ | } | ||
+ | to getLocation() {return location} | ||
+ | to getVelocity() {return velocity} | ||
+ | to hasCrashed() {return hasCrashed} | ||
+ | to hasFinished() {return hasFinished} | ||
+ | to getName() {return name} | ||
+ | to prepareForNextTurn() {instrumentPanel.prepareForNextTurn()} | ||
+ | to addAccelReactor(reactor) {accelReactors[reactor] := reactor} | ||
+ | to removeAccelReactor(reactor) {accelReactors.remove(reactor)} | ||
+ | } | ||
+ | bind instrumentPanel := makeInstrumentPanel(car) | ||
+ | return car | ||
+ | } | ||
+ | |||
+ | def makeTrackViewer(initialTextMap) { | ||
+ | def frame := <swing:makeJFrame>("Track View") | ||
+ | def mapPane := <swing:makeJTextArea>(initialTextMap) | ||
+ | def statusPane := <swing:makeJLabel>(" ") | ||
+ | def realPane := | ||
+ | JPanel`$mapPane.Y | ||
+ | $statusPane` | ||
+ | frame.getContentPane().add(realPane) | ||
+ | def windowListener { | ||
+ | to windowClosing(event) { | ||
+ | interp.continueAtTop() | ||
+ | } | ||
+ | match [verb,args] {} | ||
+ | } | ||
+ | frame.addWindowListener(windowListener) | ||
+ | frame.pack() | ||
+ | frame.show() | ||
+ | def trackViewer { | ||
+ | to refresh(textMap) {mapPane.setText(textMap)} | ||
+ | to showStatus(status) {statusPane.setText(status)} | ||
+ | } | ||
+ | return trackViewer | ||
+ | } | ||
+ | |||
+ | def makeRaceMap() { | ||
+ | def baseMap := [ | ||
+ | "..........W...............", | ||
+ | "..........W...........FFFF", | ||
+ | "......W...WW..............", | ||
+ | "......W....W..............", | ||
+ | "......W....WWW............", | ||
+ | "......W........W..........", | ||
+ | "......W.....W.............", | ||
+ | "......W.....W.............", | ||
+ | "......W...................", | ||
+ | "......W..................."] | ||
+ | |||
+ | def isWall(coord) :boolean {return baseMap[coord.getY()] [coord.getX()] == 'W' } | ||
+ | def isFinish(coord) :boolean {return baseMap[coord.getY()] [coord.getX()] == 'F'} | ||
+ | def pointCrash(coord) :boolean { | ||
+ | var result := false | ||
+ | if (coord.getX() < 0 || coord.getY() < 0 || | ||
+ | coord.getX() >= baseMap[0].size() || coord.getY() >= baseMap.size()) { | ||
+ | result := true | ||
+ | } else if (isWall(coord)) { | ||
+ | result := true | ||
+ | } | ||
+ | return result | ||
+ | } | ||
+ | def raceMap { | ||
+ | to getAsTextWithCars(cars) { | ||
+ | def embedCarsInLine(index,line) { | ||
+ | def inBounds(xLoc) :boolean {return xLoc >= 0 && xLoc < line.size()} | ||
+ | var result := line | ||
+ | for each in cars { | ||
+ | if (each.getLocation().getY() == index && | ||
+ | inBounds(each.getLocation().getX())) { | ||
+ | def editable := result.diverge(char) | ||
+ | editable[each.getLocation().getX()] := (each.getName())[0] | ||
+ | result := editable.snapshot() | ||
+ | } | ||
+ | } | ||
+ | return result | ||
+ | } | ||
+ | var result := "" | ||
+ | for i => each in baseMap { | ||
+ | result := result + embedCarsInLine(i,each) + "\n" | ||
+ | } | ||
+ | return result | ||
+ | } | ||
+ | to causesCrash(path) :boolean { | ||
+ | var result := false | ||
+ | for each in path { | ||
+ | if (pointCrash(each)) { | ||
+ | result := true | ||
+ | break() | ||
+ | } | ||
+ | } | ||
+ | return result | ||
+ | } | ||
+ | to causesFinish(path) :boolean { | ||
+ | var result := false | ||
+ | for each in path { | ||
+ | if (pointCrash(each)) { | ||
+ | break() | ||
+ | } else if (isFinish(each)) { | ||
+ | result := true | ||
+ | break() | ||
+ | } | ||
+ | } | ||
+ | return result | ||
+ | } | ||
+ | } | ||
+ | return raceMap | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * create the cars, place them in a flex map to be used as a set | ||
+ | */ | ||
+ | def makeCars(raceMap) { | ||
+ | def carList := [ | ||
+ | makeCar("1",makeCoord(1,9),raceMap), | ||
+ | makeCar("2",makeCoord(2,9),raceMap), | ||
+ | makeCar("3",makeCoord(3,9),raceMap)] | ||
+ | def carSet := [].asMap().diverge() | ||
+ | for each in carList {carSet[each] := each} | ||
+ | return carSet | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * @author </nowiki>[mailto:marcs@skyhunter.com Marc Stiegler]<nowiki> | ||
+ | */ | ||
+ | def makeRaceTrack() { | ||
+ | def raceMap := makeRaceMap() | ||
+ | def cars := makeCars(raceMap) | ||
+ | var carsReadyToMove := [].asMap().diverge() | ||
+ | def mapViewer := makeTrackViewer(raceMap.getAsTextWithCars(cars)) | ||
+ | def raceTrack { | ||
+ | to reactToAccel(car) { | ||
+ | traceln("racetrack reacting to accel") | ||
+ | carsReadyToMove[car] := car | ||
+ | if (carsReadyToMove.size() >= cars.size()) { | ||
+ | raceTrack.completeNextTurn() | ||
+ | } | ||
+ | } | ||
+ | to completeNextTurn() { | ||
+ | def winners := [].diverge() | ||
+ | for each in cars { | ||
+ | each.move() | ||
+ | if (each.hasCrashed()) { | ||
+ | cars.removeKey(each) | ||
+ | } else if (each.hasFinished()) { | ||
+ | winners.push(each) | ||
+ | } | ||
+ | } | ||
+ | mapViewer.refresh(raceMap.getAsTextWithCars(cars) ) | ||
+ | if (winners.size() == 1) { | ||
+ | mapViewer.showStatus(`Car ${winners[0].getName()} has won!`) | ||
+ | } else if (winners.size() > 1) { | ||
+ | mapViewer.showStatus("It's a tie!") | ||
+ | } else if (cars.size() == 0) { | ||
+ | mapViewer.showStatus("Everyone's dead!") | ||
+ | } else {raceTrack.prepareForNextTurn()} | ||
+ | } | ||
+ | to prepareForNextTurn() { | ||
+ | traceln("into prepare for next turn") | ||
+ | carsReadyToMove := [].asMap().diverge() | ||
+ | for each in cars { | ||
+ | each.prepareForNextTurn() | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | for each in cars {each.addAccelReactor(raceTrack)} | ||
+ | return raceTrack | ||
+ | } | ||
+ | |||
+ | makeRaceTrack() | ||
+ | # In actual code, the following line would not be commented out | ||
+ | # interp.blockAtTop()</nowiki> | ||
+ | |||
+ | </pre> |
Revision as of 00:27, 17 December 2008
Ordinary Computing Examples
Pretty E
Racetrack Game for a single computer
Below is a simple game of racetrack: 3 cars are set on the track, to race between walls around curves to the finish line. Each driver can choose to accelerate or decelerate his car by +/- 1 space/turn during each turn. Do not build up too much velocity, you won't be able to slow down!
The example has just about everything in it: JPanels, objects, functions, Java API calls, it's all there. We will come back to this example later in the book, to make the track distributed and secure. Then we shall invite Satan for a little competition, with souls on the line.
# E sample #!/usr/bin/env rune # Copyright 2002 Combex, Inc. under the terms of the MIT X license # found at http://www.opensource.org/licenses/mit-license.html pragma.syntax("0.9") def traceln(text) { stderr.println(text) } def attachAction(component,target,verb) { def listener { to actionPerformed(event) { try { E.call(target, verb, []) } catch problem { throw <- (problem) # send to E tracelog instead of AWT } } } component.addActionListener(listener) } def newButton(labelText, verb, target) { def button := <swing:makeJButton>(labelText) button.setBackground(<awt:makeSystemColor>.getControl()) attachAction(button,target,verb) return button } def abs(number) {return if (number >= 0) {number} else {-number}} def makeCoord(x,y) { def coord { to getX() {return x} to getY() {return y} to printOn(writer) {writer.print(`coord: $x,$y`)} to samePlace(coord2) :boolean { return x == coord2.getX() && y == coord2.getY() } /** * The "add" method is the underlying function for the "+" operator. * Here, by writing an "add" method, we make coordinates work with "+" */ to add(coord2) {return makeCoord(x + coord2.getX(),y + coord2.getY())} /** * The "subtract" method is the underlying function for the "-" operator */ to subtract(coord2) {return makeCoord(x - coord2.getX(),y - coord2.getY())} } return coord } def makeInstrumentPanel(car) :near { def makeIndicator(speed,positiveText,negativeText):pbc { var answer := "" var direction := positiveText if (speed < 0) {direction := negativeText} for i in 1..abs(speed) {answer := answer + direction} if (speed == 0) {answer := "0"} return answer } def makeXIndicator(speed) {return makeIndicator(speed,">","<")} def makeYIndicator(speed) {return makeIndicator(speed,"^\n","V\n")} def frame := <swing:makeJFrame>(`Car ${car.getName()} Instrument Panel`) def lbl(text) {return <swing:makeJLabel>(text)} def xLabel := lbl("Horizontal Speed:") def xIndicator := <swing:makeJTextArea>() xIndicator.setText("0") def yLabel := <swing:makeJTextArea>("V \ne\nr\nt\ni\nc\na\nl") yLabel.setBackground(<awt:makeSystemColor>.getControl()) def yIndicator := <swing:makeJTextArea>() yIndicator.setText("0") def statusPane := lbl("Running...") def instrumentPanel def btn(name,action) {return newButton(name,action,instrumentPanel)} def submitButton := btn("Submit","submit") var acceleration := makeCoord(0,0) def realPane :=JPanel` ${lbl("")} $xLabel > > > V $xIndicator > > > $yLabel.Y $yIndicator ${btn("\\","upLeft")} ${btn("^","up")} ${btn("/","upRight")} V V ${btn("<","left")} ${btn("0","zero")} ${btn(">","right")} V V ${btn("/","downLeft")} ${btn("V","down")} ${btn("\\","downRight")} V V $submitButton.X > > $statusPane > > > >` frame.setDefaultCloseOperation(<swing:makeWindowConstants>.getDO_NOTHING_ON_CLOSE()) frame.getContentPane().add(realPane) frame.pack() frame.show() bind instrumentPanel { to submit() { submitButton.setEnabled(false) car.accelerate(acceleration) } to prepareForNextTurn() { xIndicator.setText(makeXIndicator(car.getVelocity().getX())) yIndicator.setText(makeYIndicator(-(car.getVelocity().getY()))) acceleration := makeCoord(0,0) submitButton.setEnabled(true) # Note, public static transferFocus on awt Component is not Java API, added in E environment <awt:makeComponent>.transferFocus([frame.getContentPane()], statusPane) } to setStatus(status) {statusPane.setText(status)} to upLeft() {acceleration := makeCoord(-1,-1)} to up() {acceleration := makeCoord(0,-1)} to upRight() {acceleration := makeCoord(1,-1)} to left() {acceleration := makeCoord(-1,0)} to zero() {acceleration := makeCoord(0,0)} to right() {acceleration := makeCoord(1,0)} to downLeft() {acceleration := makeCoord(-1,1)} to down() {acceleration := makeCoord(0,1)} to downRight() {acceleration := makeCoord(1,1)} } return instrumentPanel } def makeCar(name,startLocation,raceMap) { var location := startLocation var acceleration := makeCoord(0,0) var velocity := makeCoord(0,0) var hasCrashed := false var hasFinished := false def instrumentPanel def sign(x) { return if (x > 0) { 1 } else if (x < 0) { -1 } else {0} } def accelReactors := [].asMap().diverge() /** * Compute the path the car will take from the location at the * beginning of this turn to the end; return the result * as a list of coords */ def computeIntermediateLocations(start,finish) { def locations := [].diverge() def slope := (finish.getY() - start.getY()) / (finish.getX() - start.getX()) def computeRemainingLocations(current) { var nextX := current.getX() var nextY := current.getY() var distToGo := 0 #if the car is traveling faster in the x direction than #in the y direction, increment x position by one along #the path and compute the new y if (slope < 1.0 && slope > -1.0) { distToGo := finish.getX() - current.getX() nextX += sign(distToGo) def distTraveled := nextX - start.getX() nextY := start.getY() + ((slope * distTraveled) + 0.5) //1 #if the car is traveling faster in the y direction than #in the x direction, increment y position by one along #the path and compute new x } else { distToGo := finish.getY() - current.getY() nextY += sign(distToGo) def distTraveled := nextY - start.getY() nextX := start.getX() + ((distTraveled/slope) + 0.5) //1 } def next := makeCoord(nextX,nextY) locations.push(next) if (!(next.samePlace(finish))) { computeRemainingLocations(next) } } computeRemainingLocations(start) return locations } def car { to accelerate(accel) { traceln(`accelerating car $name`) acceleration := accel for each in accelReactors { each.reactToAccel(car) } } to move(){ traceln("into move") velocity += acceleration def newLocation := location + velocity traceln("got newlocation") def path := computeIntermediateLocations(location,newLocation) location := newLocation traceln("assigned location") hasCrashed := hasCrashed || raceMap.causesCrash(path) hasFinished := hasFinished || raceMap.causesFinish(path) traceln("got crash finish") if (hasCrashed) { instrumentPanel.setStatus("Crashed") } else if (hasFinished) {instrumentPanel.setStatus("Finished")} traceln("out of move") } to getLocation() {return location} to getVelocity() {return velocity} to hasCrashed() {return hasCrashed} to hasFinished() {return hasFinished} to getName() {return name} to prepareForNextTurn() {instrumentPanel.prepareForNextTurn()} to addAccelReactor(reactor) {accelReactors[reactor] := reactor} to removeAccelReactor(reactor) {accelReactors.remove(reactor)} } bind instrumentPanel := makeInstrumentPanel(car) return car } def makeTrackViewer(initialTextMap) { def frame := <swing:makeJFrame>("Track View") def mapPane := <swing:makeJTextArea>(initialTextMap) def statusPane := <swing:makeJLabel>(" ") def realPane := JPanel`$mapPane.Y $statusPane` frame.getContentPane().add(realPane) def windowListener { to windowClosing(event) { interp.continueAtTop() } match [verb,args] {} } frame.addWindowListener(windowListener) frame.pack() frame.show() def trackViewer { to refresh(textMap) {mapPane.setText(textMap)} to showStatus(status) {statusPane.setText(status)} } return trackViewer } def makeRaceMap() { def baseMap := [ "..........W...............", "..........W...........FFFF", "......W...WW..............", "......W....W..............", "......W....WWW............", "......W........W..........", "......W.....W.............", "......W.....W.............", "......W...................", "......W..................."] def isWall(coord) :boolean {return baseMap[coord.getY()] [coord.getX()] == 'W' } def isFinish(coord) :boolean {return baseMap[coord.getY()] [coord.getX()] == 'F'} def pointCrash(coord) :boolean { var result := false if (coord.getX() < 0 || coord.getY() < 0 || coord.getX() >= baseMap[0].size() || coord.getY() >= baseMap.size()) { result := true } else if (isWall(coord)) { result := true } return result } def raceMap { to getAsTextWithCars(cars) { def embedCarsInLine(index,line) { def inBounds(xLoc) :boolean {return xLoc >= 0 && xLoc < line.size()} var result := line for each in cars { if (each.getLocation().getY() == index && inBounds(each.getLocation().getX())) { def editable := result.diverge(char) editable[each.getLocation().getX()] := (each.getName())[0] result := editable.snapshot() } } return result } var result := "" for i => each in baseMap { result := result + embedCarsInLine(i,each) + "\n" } return result } to causesCrash(path) :boolean { var result := false for each in path { if (pointCrash(each)) { result := true break() } } return result } to causesFinish(path) :boolean { var result := false for each in path { if (pointCrash(each)) { break() } else if (isFinish(each)) { result := true break() } } return result } } return raceMap } /** * create the cars, place them in a flex map to be used as a set */ def makeCars(raceMap) { def carList := [ makeCar("1",makeCoord(1,9),raceMap), makeCar("2",makeCoord(2,9),raceMap), makeCar("3",makeCoord(3,9),raceMap)] def carSet := [].asMap().diverge() for each in carList {carSet[each] := each} return carSet } /** * @author [mailto:marcs@skyhunter.com Marc Stiegler] */ def makeRaceTrack() { def raceMap := makeRaceMap() def cars := makeCars(raceMap) var carsReadyToMove := [].asMap().diverge() def mapViewer := makeTrackViewer(raceMap.getAsTextWithCars(cars)) def raceTrack { to reactToAccel(car) { traceln("racetrack reacting to accel") carsReadyToMove[car] := car if (carsReadyToMove.size() >= cars.size()) { raceTrack.completeNextTurn() } } to completeNextTurn() { def winners := [].diverge() for each in cars { each.move() if (each.hasCrashed()) { cars.removeKey(each) } else if (each.hasFinished()) { winners.push(each) } } mapViewer.refresh(raceMap.getAsTextWithCars(cars) ) if (winners.size() == 1) { mapViewer.showStatus(`Car ${winners[0].getName()} has won!`) } else if (winners.size() > 1) { mapViewer.showStatus("It's a tie!") } else if (cars.size() == 0) { mapViewer.showStatus("Everyone's dead!") } else {raceTrack.prepareForNextTurn()} } to prepareForNextTurn() { traceln("into prepare for next turn") carsReadyToMove := [].asMap().diverge() for each in cars { each.prepareForNextTurn() } } } for each in cars {each.addAccelReactor(raceTrack)} return raceTrack } makeRaceTrack() # In actual code, the following line would not be commented out # interp.blockAtTop()