Walnut/Ordinary Programming/Ordinary Computing Examples
From Erights
(Difference between revisions)
(FIELD_OTHER) |
m (Reverted edits by 210.52.15.210 (Talk); changed back to last version by Kevin Reid) |
||
| (7 intermediate revisions not shown) | |||
| 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> | ||
Latest revision as of 16:13, 18 January 2009
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()

