Walnut/Ordinary Programming/Ordinary Computing Examples
From Erights
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()

