Added a range of original TouchDevelop scripts converted to .ts for tests (#283)

This commit is contained in:
David Whale 2016-10-23 05:32:57 +01:00 committed by Peli de Halleux
parent 8089fe262e
commit 2f11d603e7
10 changed files with 3212 additions and 0 deletions

View File

@ -0,0 +1,222 @@
let AWasPressed: boolean
let BWasPressed: boolean
let ABWasPressed: boolean
let wasShake: boolean
let scoreA: number
let scoreB: number
scoreA = 0
scoreB = 0
startIOMonitor()
let gameTime = getGameTime()
basic.showLeds(`
. . # . .
. . # . .
. . # # #
. . . . .
. . . . .
`)
while (!BWasPressed) {
basic.pause(100)
}
BWasPressed = false
playOneGame(gameTime)
showFinalScores(scoreA, scoreB)
function startIOMonitor() {
input.onButtonPressed(Button.A, () => {
AWasPressed = true
})
input.onButtonPressed(Button.B, () => {
BWasPressed = true
})
input.onButtonPressed(Button.AB, () => {
ABWasPressed = true
AWasPressed = false
BWasPressed = false
})
input.onShake(() => {
wasShake = true
})
AWasPressed = false
BWasPressed = false
ABWasPressed = false
wasShake = false
}
/**
* display score for A and B on same screen as a graphic
* this shows a tug of war line, in the middle if scores the same,
* Can cope with differences +/-10
* @param scoreA1 TODO
* @param scoreB1 TODO
*/
function showScore(scoreA1: number, scoreB1: number) {
let img = images.createImage(`
# . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . #
# . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . #
# . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . #
# . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . #
# . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . #
`)
let diff = Math.clamp(-10, 10, scoreB1 - scoreA1)
diff = diff + 10
img.plotFrame(diff)
}
/**
* show digits 0..10
* @param digits TODO
*/
function showDigits(digits: number) {
digits = Math.clamp(0, 10, digits)
let img = images.createImage(`
. . # . . . . # . . . # # . . . # # . . . # . . . . # # # . . . # # . . # # # . . . # . . . . # . . # . . # .
. # . # . . # # . . . . . # . . . . # . . # . . . . # . . . . # . . . . . . # . . # . # . . # . # . # . # . #
. # . # . . . # . . . . # . . . . # . . . # # # . . . # # . . # # . . . . # . . . . # . . . . # # . # . # . #
. # . # . . . # . . . # . . . . . . # . . . # . . . . . # . . # . # . . # . . . . # . # . . . . # . # . # . #
. . # . . . # # # . . # # # . . # # . . . . # . . . # # . . . . # . . . # . . . . . # . . . # # . . # . . # .
`)
img.plotFrame(digits)
}
/**
* show time graphic for time remaining
* @param gameTime TODO
*/
function showTime(gameTime: number) {
let minutes = Math.clamp(0, 10, gameTime / 60)
let seconds = gameTime % 60
// divide seconds into 10 second stripes
let stripes = seconds / 10
let img = images.createImage(`
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . # . . . . # # . . . # # # . . # # # # . # # # # #
. . . . . # . . . . # # . . . # # # . . # # # # . # # # # #
`)
img.plotFrame(stripes)
// leave middle row blank
// display up to 10 dots in raster on top two rows
if (minutes > 0) {
for (let i = 0; i < minutes; i++) {
let y = i / 5
let x = i % 5
led.plot(x, y)
}
}
}
function getGameTime(): number {
let chosenGameTime = 7
showDigits(chosenGameTime)
while (!BWasPressed) {
if (AWasPressed) {
if (chosenGameTime < 10) {
chosenGameTime = chosenGameTime + 1
} else {
chosenGameTime = 1
}
showDigits(chosenGameTime)
AWasPressed = false
} else {
basic.pause(100)
}
}
BWasPressed = false
return chosenGameTime
}
function playOneGame(gameTime: number) {
let gameStartTime = input.runningTime()
let gameElapsedTime = 0
let gameTimeRemaining = gameTime * 60
let timeout = 0
let lastDisplayedTime = 0
showScore(scoreA, scoreB)
let state = "TIME"
while (gameTimeRemaining >= 0) {
// Tick the game time
gameElapsedTime = (input.runningTime() - gameStartTime) / 1000
gameTimeRemaining = gameTime * 60 - gameElapsedTime
// Handle any global events such as point buttons
if (AWasPressed) {
AWasPressed = false
scoreA = scoreA + 1
if (state != "LAST10") {
showScore(scoreA, scoreB)
state = "SCORE"
}
} else if (BWasPressed) {
BWasPressed = false
scoreB = scoreB + 1
if (state != "LAST10") {
showScore(scoreA, scoreB)
state = "SCORE"
}
}
// Handle global transitions
if (gameTimeRemaining <= 10 && state != "LAST10") {
state = "LAST10"
}
// Handle game states
if (state == "SCORE") {
if (wasShake) {
wasShake = false
showTime(gameTimeRemaining)
lastDisplayedTime = gameTimeRemaining
timeout = input.runningTime() + 5 * 1000
state = "TIME"
}
} else if (state == "TIME") {
if (input.runningTime() > timeout) {
showScore(scoreA, scoreB)
state = "SCORE"
}
} else if (state == "LAST10") {
if (gameTimeRemaining != lastDisplayedTime) {
showDigits(gameTimeRemaining)
lastDisplayedTime = gameTimeRemaining
}
}
basic.pause(100)
}
}
function showFinalScores(scoreA1: number, scoreB1: number) {
basic.showLeds(`
# . . . #
. # . # .
. . # . .
. # . # .
# . . . #
`)
while (true) {
if (AWasPressed) {
basic.showString("A", 150)
basic.showNumber(scoreA1, 150)
basic.showLeds(`
# . . . #
. # . # .
. . # . .
. # . # .
# . . . #
`)
AWasPressed = false
} else if (BWasPressed) {
basic.showString("B", 150)
basic.showNumber(scoreB1, 150)
basic.showLeds(`
# . . . #
. # . # .
. . # . .
. # . # .
# . . . #
`)
BWasPressed = false
} else {
basic.pause(100)
}
}
}

View File

@ -0,0 +1,248 @@
let speed: number
let cupCapacity: number
let maxMisses: number
let autoEmpty: boolean
let movement: boolean
let sensitivity: number
let cupX: number
let cupInverted: boolean
let highscore = 0
while (true) {
// configure game settings
// ##CHALLENGE1: reconfigure game
cupCapacity = 5
speed = 6
maxMisses = 3
autoEmpty = false
movement = true
sensitivity = 400
cupX = 2
// show the spash screen
// ##CHALLENGE 2: CHANGE SPLASH SCREEN
basic.showAnimation(`
. . . . . . . . . .
. . . . . . # . # .
. . . . . . . . . .
. # . # . . # . # .
. # # # . . # # # .
`, 400)
// Decide what to do based on which button is pressed
if (input.buttonIsPressed(Button.A)) {
let finalScore = playGame()
// ##CHALLENGE 3 ADD HIGH SCORE
if (finalScore > highscore) {
basic.showString("HIGH", 150)
highscore = finalScore
}
basic.showNumber(finalScore, 150)
} else if (input.buttonIsPressed(Button.B)) {
testMovement()
} else {
basic.pause(100)
}
}
function playGame(): number {
let cup = images.createImage(`
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . # . # . . . . . .
. . . . . . # # # . . . . . .
`)
let score = 0
let dropsInCup = 0
let misses = 0
let dropX = 0
let dropY = 0
let prevDropY = -1
let cupX1 = 2
let prevCupX = -1
let state = "NEWDROP"
startGame()
while (true) {
if (state == "NEWDROP") {
// create a new drop at a random position
dropX = Math.random(5)
dropY = 0
state = "RAINING"
} else if (state == "RAINING") {
// calculate new positions
cupX1 = getCupPosition()
let thisDropY = dropY / speed
// Only redraw the screen if something has changed (prevent flashing)
if (cupX1 != prevCupX || thisDropY != prevDropY) {
basic.clearScreen()
// draw cup
cup.showImage(7 - cupX1)
if (dropsInCup == cupCapacity) {
// a full cup
led.plot(cupX1, 3)
}
// draw drop
led.plot(dropX, thisDropY)
prevCupX = cupX1
prevDropY = thisDropY
}
basic.pause(100)
if (thisDropY >= 4) {
state = "ATCUP"
} else {
dropY = dropY + 1
}
if (cupInverted && dropsInCup >= cupCapacity) {
state = "EMPTYING"
}
} else if (state == "ATCUP") {
if (dropX == cupX1) {
state = "CATCH"
} else {
state = "MISS"
}
} else if (state == "MISS") {
// ##CHALLENGE: long beep on miss
beep(500)
misses = misses + 1
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . .
. . . . . . # . # . . . . . . . . . . .
. . . . . . # # # . . # . # . . . . . .
. # . # . . . . . . . # # # . . # . # .
. # # # . . . # . . . . . . . . # # # .
`, 400)
if (misses > maxMisses) {
state = "GAMEOVER"
} else {
state = "NEWDROP"
}
} else if (state == "CATCH") {
// ##CHALLENGE: short beep on catch
beep(200)
dropsInCup = dropsInCup + 1
basic.showAnimation(`
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. # # # . # . # . # . # . # .
. # # # . # # # # # . # # # .
`, 400)
if (dropsInCup == cupCapacity) {
state = "FULL"
score = score + 1
} else if (dropsInCup > cupCapacity) {
state = "OVERFLOW"
} else {
score = score + 1
state = "NEWDROP"
}
} else if (state == "FULL") {
basic.showAnimation(`
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . # # # . . . . . .
. # # # . . # # # . . # # # .
. # # # . . # # # . . # # # .
`, 400)
if (autoEmpty) {
state = "EMPTYING"
} else {
state = "NEWDROP"
}
} else if (state == "EMPTYING") {
if (cupInverted) {
basic.showAnimation(`
. . . . . . . . . . . . . . . . . # . . . . . . .
. . . . . . . . . . . . # . . . . . . . . . . . .
. . . . . . . # . . . . . . . . . . . . . . . . .
. # # # . . # . # . . # . # . . # . # . . # . # .
. # # # . . # # # . . # # # . . # # # . . # # # .
`, 400)
} else {
basic.showAnimation(`
. . . . . . . . . . . # # # . . # # # . . # # # . . # # # . . # # # . . . . . . . . . . .
. . . . . . # # . . . # # # . . # . # . . # . # . . # . # . . # . # . . # # . . . . . . .
. . . . . . # # . . . . . . . . . # . . . . . . . . . . . . . . . . . . # . . . . . . . .
. # # # . . # # . . . . . . . . . . . . . . # . . . . . . . . . . . . . # # . . . # . # .
. # # # . . . . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . # # # .
`, 400)
}
dropsInCup = 0
// ##CHALLENGE: Speed up on every level change
if (speed > 1) {
speed = speed - 1
}
state = "NEWDROP"
} else if (state == "OVERFLOW") {
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . # # # . . # . # . . . . . . . . . . . . . . . .
. # # # . . # # # . # # # # # # # # # # . # # # . . # # # .
. # # # . . # # # . . # # # . . # # # . # # # # # . # # # .
`, 400)
state = "GAMEOVER"
} else if (state == "GAMEOVER") {
// ##CHALLENGE: Make a sound on game over
beep(700)
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . # # # # # . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . # # # # # . # . # . . . . . . . . . . . . # . # . . . . . .
. . . . . . . . . . . . . . . . . . . . . # # # . # # # # # . # . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. # . # . . . . . . . # # # . # # # # # . # . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. # # # . # # # # # . # . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
`, 400)
break
}
}
return score
}
function testMovement() {
while (!input.buttonIsPressed(Button.A)) {
// ##CHALLENGE 5: Add accelerometer test mode
let x = getCupPosition()
basic.clearScreen()
led.plot(x, 2)
basic.pause(200)
}
}
function getCupPosition(): number {
if (movement) {
let acc = input.acceleration(Dimension.X) / sensitivity
cupX = Math.clamp(0, 4, acc + 2)
return cupX
}
if (input.buttonIsPressed(Button.A)) {
if (cupX > 0) {
cupX = cupX - 1
}
} else if (input.buttonIsPressed(Button.B)) {
if (cupX < 4) {
cupX = cupX + 1
}
}
return cupX
}
function startGame() {
basic.clearScreen()
// If button still held from start-game, wait until it is released
while (input.buttonIsPressed(Button.A)) {
// wait for button to be released
}
// handlers that work out if cup is turned upside down or not
input.onLogoDown(() => {
cupInverted = true
})
input.onLogoUp(() => {
cupInverted = false
})
}
function beep(p: number) {
pins.digitalWritePin(DigitalPin.P0, 1)
basic.pause(p)
pins.digitalWritePin(DigitalPin.P0, 0)
}

716
tests/wg-dr-who.ts Normal file
View File

@ -0,0 +1,716 @@
let AWasPressed: boolean
let BWasPressed: boolean
let ABWasPressed: boolean
let wasShake: boolean
startIOMonitor()
let gameNo = 0
while (true) {
// select a new game
if (AWasPressed) {
gameNo = (gameNo + 1) % 4
AWasPressed = false
}
// show selected game
if (gameNo == 0) {
basic.showLeds(`
# # # # #
# . . . #
. # . # .
. . . . .
. # # # .
`)
} else if (gameNo == 1) {
basic.showLeds(`
# # # . .
# . . . .
# # # # #
# # . . .
# # # . .
`)
} else if (gameNo == 2) {
basic.showLeds(`
. . . . .
# . . . .
# # # # #
# . . . .
. . . . .
`)
} else if (gameNo == 3) {
basic.showLeds(`
# . # # #
# # # # #
. . . # #
# # # # #
# # # # #
`)
}
// start selected game
if (BWasPressed) {
// Play the selected game
basic.clearScreen()
waitBReleased()
resetButtons()
let finalScore = 0
basic.clearScreen()
if (gameNo == 0) {
finalScore = playCybermen()
} else if (gameNo == 1) {
finalScore = playDalek()
} else if (gameNo == 2) {
finalScore = playSonicScrewdriver()
} else if (gameNo == 3) {
finalScore = playJudoonLanguage()
}
flashDigit(finalScore, 3)
resetButtons()
waitBPressed()
basic.clearScreen()
waitBReleased()
resetButtons()
} else {
basic.pause(100)
}
}
/**
* Game parameters
* Percentage chance that cyberman will move left/right to line up with you
*/
function playCybermen(): number {
let maxGameTime = 60
let cybermanMoveXProbability = 20
// How long (in 100ms ticks) cyberman must line up before moving forward
let cybermanMoveYCount = 10
// Game variables
let startTime = input.runningTime()
let gameTime = 0
let playerX = 2
let cybermanX = 1
let cybermanY = 0
let cybermanLineupCount = 0
let redraw = true
while (gameTime < maxGameTime) {
if (redraw) {
basic.clearScreen()
led.plot(playerX, 4)
led.plot(cybermanX, cybermanY)
redraw = false
}
// Handle Player Movement
if (AWasPressed) {
if (playerX > 0) {
playerX = playerX - 1
redraw = true
}
AWasPressed = false
} else if (BWasPressed) {
if (playerX < 4) {
playerX = playerX + 1
redraw = true
}
BWasPressed = false
}
// Handle Cyberman line-of-sight checking
if (cybermanX == playerX) {
cybermanLineupCount = cybermanLineupCount + 1
if (cybermanLineupCount >= cybermanMoveYCount) {
if (cybermanY == 4) {
// Cyberman caught you, game over
break
} else {
cybermanY = cybermanY + 1
redraw = true
cybermanLineupCount = 0
}
}
} else {
cybermanLineupCount = 0
// Move Cyberman closer to player, slowly
if (Math.random(100) < cybermanMoveXProbability) {
if (cybermanX > playerX) {
cybermanX = cybermanX - 1
} else if (cybermanX < playerX) {
cybermanX = cybermanX + 1
}
redraw = true
}
}
basic.pause(100)
gameTime = (input.runningTime() - startTime) / 1000
}
return convertSurvivalTimeToScore(gameTime)
}
/**
* Game parameters, all probabilities as a percentage
*/
function playDalek(): number {
let maxGameTime = 60
let userMoveSensitivity = 40
let dalekMoveSensitivity = 20
let dalekShootChance = 10
let dalekHitChance = 10
// Game variables
let gameTime = 0
let startTime = input.runningTime()
let dalekY = 2
let userY = 1
let redraw = true
while (gameTime < maxGameTime) {
// Redraw screen if necessary
if (redraw) {
basic.clearScreen()
led.plot(0, dalekY)
led.plot(4, userY)
redraw = false
}
// Work out if the user has moved, and move them
let tilt = getTilt()
if (tilt > 2) {
// Moving up, slowly
if (userY < 4) {
if (Math.random(100) < userMoveSensitivity) {
userY = userY + 1
redraw = true
}
}
} else if (tilt < 2) {
// Moving down (slowly)
if (userY > 0) {
if (Math.random(100) < userMoveSensitivity) {
userY = userY - 1
redraw = true
}
}
}
// Move the Dalek to line up with user
if (dalekY < userY) {
if (Math.random(100) < dalekMoveSensitivity) {
dalekY = dalekY + 1
redraw = true
}
} else if (dalekY > userY) {
if (Math.random(100) < dalekMoveSensitivity) {
dalekY = dalekY - 1
redraw = true
}
} else {
// Dalek lines up
if (Math.random(100) < dalekShootChance) {
// Shoot a raygun at the user
for (let i = 0; i < 3; i++) {
led.plot(i + 1, dalekY)
basic.pause(100)
}
if (Math.random(100) < dalekHitChance) {
// User has been hit, game over
break
}
redraw = true
}
}
gameTime = (input.runningTime() - startTime) / 1000
basic.pause(100)
}
return convertSurvivalTimeToScore(gameTime)
}
/**
* Set this in steps of 60
*/
function playSonicScrewdriver(): number {
let maxGameTime = 120
let gameTime = 0
// @=0, A=1 etc
// bit0=N, bit1=E, bit2=S, bit3=W
// bit=0 means it is a wall (can not be opened)
// bit=1 means it is a door (can be opened)
let mazestr = "BLFL@GOIFIGLCJIA"
let maze = ([] as number[])
// Locks use same number encoding (bits 0,1,2,3)
// bit=0 means door is locked (cannot be walked through)
// bit=1 means door is open (can be walked through)
let doorOpen = ([] as number[])
for (let i = 0; i < 16; i++) {
doorOpen.push(0)
maze.push(mazestr.charCodeAt(i) - 64)
}
let redraw = true
let cellno = 0
let direction = "N"
let startTime = input.runningTime()
while (gameTime < maxGameTime) {
// Draw the screen
if (redraw) {
basic.clearScreen()
// Always draw the maze with N at the top
drawMazeCell(doorOpen[cellno])
// draw user standing next to selected wall
if (direction == "N") {
led.plot(2, 1)
} else if (direction == "E") {
led.plot(3, 2)
} else if (direction == "S") {
led.plot(2, 3)
} else if (direction == "W") {
led.plot(1, 2)
}
redraw = false
}
// Sense any button presses
if (AWasPressed) {
if (direction == "N") {
direction = "E"
} else if (direction == "E") {
direction = "S"
} else if (direction == "S") {
direction = "W"
} else if (direction == "W") {
direction = "N"
}
redraw = true
AWasPressed = false
} else if (BWasPressed) {
// Try to walk through an open door
if (isDoorOpen(doorOpen[cellno], direction)) {
cellno = mazeForward(cellno, 4, 4, direction)
redraw = true
}
BWasPressed = false
} else if (wasShake) {
// energise sonic screwdriver
if (isDoorOpen(maze[cellno], direction)) {
// It is a door, but is the door open or closed?
if (!(isDoorOpen(doorOpen[cellno], direction))) {
// Open the door in front of us
doorOpen[cellno] = openDoor(doorOpen[cellno], direction)
// Also open the return door for when we walk through this door
let forwardCellno = mazeForward(cellno, 4, 4, direction)
doorOpen[forwardCellno] = openDoor(doorOpen[forwardCellno], opposite(direction))
}
} else {
// Not a door
basic.showLeds(`
. . . . .
. # . # .
. . # . .
. # . # .
. . . . .
`)
basic.pause(500)
}
redraw = true
wasShake = false
}
if (cellno == 15) {
// Have reached the exit cell
break
}
basic.pause(100)
gameTime = (input.runningTime() - startTime) / 1000
}
return convertPenaltyTimeToScore(gameTime / maxGameTime / 60)
}
function playJudoonLanguage(): number {
let maxGameTime = 60
let gameTime = 0
let startTime = input.runningTime()
let font = images.createImage(`
# . # # # # . # . # . . . . # # # . . . # # # # # # . # . # # # # . . . . . # # # . . . # # # # # #
# # # # # # # # # # . . . # # . # # # # . # . . . # # # # # # # # # # # # # # . # # # # # . . # . .
. . . . # # # # . . . . # # # . . . . # . # . . . . . . . # . . . # # . . # # # . . # . . # # # # #
# # # # # # # # # # . . # . . # # # # # # # # # # # # # # # # # # # . # # # # # # # # # # # # # . .
# # # # # # # # # # # # # # # # . . . . # . . . # # . . . . . . . # # # # # . . # # # # # # # # # #
`)
let answers = images.createImage(`
# # # # # # # # . . . # # # . . . # . . # . # . #
# . . . # # . . . . # # # # # . . . . . . . . . .
. # . # . # # # # . # . # . # . # . # . # . # . #
. . . . . # # # . . # . # . # . . . . . . . . . .
. # # # . # # # . . # . # . # # # # # # # . . . #
`)
let pages = "029 041 167 208 283"
let actualAnswer = Math.random(5)
let pos = 0
let redraw = true
while (gameTime < maxGameTime) {
// Draw the current frame from pos (0,1,2 codeword, 3,4,5,6,7 answers)
if (redraw) {
if (pos <= 2) {
// Draw codeword symbol for this choice and this position
let digit = parseInt(pages[actualAnswer * 4 + pos])
font.plotFrame(digit)
} else {
// Draw answer
let item = pos - 3
answers.plotFrame(item)
}
redraw = false
}
// Process button presses
if (AWasPressed) {
// Move left, unless at far left already
AWasPressed = false
if (pos > 0) {
pos = pos - 1
redraw = true
}
} else if (BWasPressed) {
// Move right, unless already at far right
BWasPressed = false
if (pos < 7) {
pos = pos + 1
redraw = true
}
} else if (wasShake) {
// User trying to select an answer, are we at an answer position?
wasShake = false
if (pos >= 3) {
// Is it the right answer?
let userChoice = pos - 3
if (userChoice == actualAnswer) {
// CORRECT
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . #
. . . . . . . . . . . . . . . . . . # . . . . # .
# . . . . # . . . . # . # . . # . # . . # . # . .
. . . . . . # . . . . # . . . . # . . . . # . . .
`, 400)
basic.pause(1000)
break
} else {
// WRONG
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . . . # # . . . # # . . . # # . . . # # . . . #
. . . . . . . . . . . . . . . . . . # . . . . # . . . . # . . # . # . . # . # . . # . # .
. . . . . . . . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . .
. . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . # . . # . # .
# . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . #
`, 400)
basic.pause(1000)
redraw = true
}
}
}
gameTime = (input.runningTime() - startTime) / 1000
basic.pause(100)
}
return convertPenaltyTimeToScore(gameTime)
}
function getDirection(): string {
let bearing = input.compassHeading()
if (bearing < 45 || bearing > 315) {
return "N"
} else if (bearing < 135) {
return "E"
} else if (bearing < 225) {
return "S"
} else {
return "W"
}
}
function calibrateCompass() {
if (input.compassHeading() == -4) {
input.calibrate()
}
}
function waitBReleased() {
while (input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
}
function waitBPressed() {
while (!input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
}
/**
* Show the score 0..9
* @param digit TODO
* @param times TODO
*/
function flashDigit(digit: number, times: number) {
digit = Math.clamp(0, 9, digit)
for (let i = 0; i < times; i++) {
basic.showNumber(digit, 0)
basic.pause(500)
basic.clearScreen()
basic.pause(500)
}
basic.showNumber(digit, 0)
}
/**
* score is calculated as the amount of time you lasted
* @param gameTime TODO
*/
function convertSurvivalTimeToScore(gameTime: number): number {
if (gameTime <= 4) {
return 0
} else if (gameTime <= 9) {
return 1
} else if (gameTime <= 14) {
return 2
} else if (gameTime <= 19) {
return 3
} else if (gameTime <= 24) {
return 4
} else if (gameTime <= 29) {
return 5
} else if (gameTime <= 39) {
return 6
} else if (gameTime <= 49) {
return 7
} else if (gameTime <= 59) {
return 8
} else {
return 9
}
}
function convertPenaltyTimeToScore(penaltyTime: number): number {
if (penaltyTime <= 4) {
return 9
} else if (penaltyTime <= 9) {
return 8
} else if (penaltyTime <= 14) {
return 7
} else if (penaltyTime <= 19) {
return 6
} else if (penaltyTime <= 24) {
return 5
} else if (penaltyTime <= 29) {
return 4
} else if (penaltyTime <= 39) {
return 3
} else if (penaltyTime <= 49) {
return 2
} else if (penaltyTime <= 59) {
return 1
} else {
return 0
}
}
function startIOMonitor() {
input.onButtonPressed(Button.A, () => {
AWasPressed = true
})
input.onButtonPressed(Button.B, () => {
BWasPressed = true
})
input.onButtonPressed(Button.AB, () => {
ABWasPressed = true
})
input.onShake(() => {
wasShake = true
})
AWasPressed = false
BWasPressed = false
ABWasPressed = false
wasShake = false
}
/**
* maze is always drawn with north at top
* @param cell TODO
*/
function drawMazeCell(cell: number) {
let n = !(isNorth(cell))
let e = !(isEast(cell))
let s = !(isSouth(cell))
let w = !(isWest(cell))
// Draw any visible walls
if (n) {
for (let i = 0; i < 5; i++) {
led.plot(i, 0)
}
}
if (e) {
for (let l = 0; l < 5; l++) {
led.plot(4, l)
}
}
if (s) {
for (let k = 0; k < 5; k++) {
led.plot(k, 4)
}
}
if (w) {
for (let j = 0; j < 5; j++) {
led.plot(0, j)
}
}
}
/**
* work out the cell number in front of this cell
* given the direction N E S W (N points to button B)
* returns the forward cell number, -1 if outside of maze
* Turn cellno into an x and y based on width and height
* @param cellno TODO
* @param width TODO
* @param height TODO
* @param direction TODO
*/
function mazeForward(cellno: number, width: number, height: number, direction: string): number {
let y = cellno / width
let x = cellno % width
// Work out change in x/y and therefore change in cellno
// But bounds-check against width and height
// as user cannot walk outside of the maze
if (direction == "N") {
// sub 1 from y
if (y > 0) {
return cellno - width
}
} else if (direction == "E") {
// Add 1 to x
if (x < width - 1) {
return cellno + 1
}
} else if (direction == "S") {
// add 1 to y
if (y < height - 1) {
return cellno + width
}
} else if (direction == "W") {
// sub 1 from x
if (x > 0) {
return cellno - 1
}
}
// Not allowed to move in this direction, it will go outside of maze
return - 1
}
/**
* A door is open if the lock bit is 1
* A door is present if the maze bit is 1
* @param cell TODO
* @param direction TODO
*/
function isDoorOpen(cell: number, direction: string): boolean {
if (direction == "N") {
return isNorth(cell)
} else if (direction == "E") {
return isEast(cell)
}
if (direction == "S") {
return isSouth(cell)
} else if (direction == "W") {
return isWest(cell)
}
return false
}
function getTilt(): number {
let tilt: number
tilt = input.acceleration(Dimension.Y)
tilt = Math.clamp(-1024, 1023, tilt)
tilt = (tilt + 1024) / 512
return tilt
}
function waitAReleased() {
while (input.buttonIsPressed(Button.A)) {
basic.pause(100)
}
}
function waitNoButtons() {
while (input.buttonIsPressed(Button.A) || input.buttonIsPressed(Button.B) || input.buttonIsPressed(Button.AB)) {
basic.pause(100)
}
}
function resetButtons() {
AWasPressed = false
BWasPressed = false
ABWasPressed = false
}
/**
* The appropriate bit (0,1,2,3) is set, which unlocks the door
* @param cell TODO
* @param direction TODO
*/
function openDoor(cell: number, direction: string): number {
if (direction == "N") {
return cell | 1
} else if (direction == "E") {
return cell | 2
} else if (direction == "S") {
return cell | 4
} else if (direction == "W") {
return cell | 8
}
return cell
}
/**
* is the north bit set in the cell?
* @param cell TODO
*/
function isNorth(cell: number): boolean {
if ((cell & 1) != 0) {
return true
}
return false
}
/**
* is the east bit set in the cell?
* @param cell TODO
*/
function isEast(cell: number): boolean {
if ((cell & 2) != 0) {
return true
}
return false
}
/**
* is the south bit set in the cell?
* @param cell TODO
*/
function isSouth(cell: number): boolean {
if ((cell & 4) != 0) {
return true
}
return false
}
/**
* is the west bit set in the cell?
* @param cell TODO
*/
function isWest(cell: number): boolean {
if ((cell & 8) != 0) {
return true
}
return false
}
function opposite(direction: string): string {
if (direction == "N") {
return "S"
} else if (direction == "E") {
return "W"
} else if (direction == "S") {
return "N"
} else if (direction == "W") {
return "E"
}
return direction
}
function isSount() { }

165
tests/wg-flood-monitor.ts Normal file
View File

@ -0,0 +1,165 @@
let sensorType: string
// ANALOG or TILT
sensorType = "TILT"
while (true) {
// splash screen (flood monitor)
basic.showLeds(`
# # # . .
# . . . .
# # . # .
# . . # .
# . . # #
`)
while (true) {
if (input.buttonIsPressed(Button.A)) {
// test that the sensor works
testMode()
break
} else if (input.buttonIsPressed(Button.B)) {
// run the real flood monitor
floodMonitor()
break
} else {
basic.pause(100)
}
}
}
/**
* test mode - test that the monitor and buzzer work
* no filtering in this mode, direct screen update
*/
function testMode() {
basic.showLeds(`
# # # # #
. . # . .
. . # . .
. . # . .
. . # . .
`)
waitNoButtons()
let img = images.createImage(`
. . . . . . . . . . . . . . . . . . . . # # # # #
. . . . . . . . . . . . . . . # # # # # . . . . .
. . . . . . . . . . # # # # # . . . . . . . . . .
. . . . . # # # # # . . . . . . . . . . . . . . .
# # # # # . . . . . . . . . . . . . . . . . . . .
`)
while (!input.buttonIsPressed(Button.A)) {
// Show live reading on display
let value = readSensor()
img.showImage(value * 5)
// Turn beeper on only if maximum value seen
if (value >= 4) {
pins.digitalWritePin(DigitalPin.P1, 1)
} else {
pins.digitalWritePin(DigitalPin.P1, 0)
}
basic.pause(50)
}
waitNoButtons()
}
function floodMonitor() {
basic.showLeds(`
# . . . #
# # . # #
# . # . #
# . . . #
# . . . #
`)
waitNoButtons()
let line = images.createImage(`
. . . . . . . . . . . . . . . . . . . . # # # # #
. . . . . . . . . . . . . . . # # # # # . . . . .
. . . . . . . . . . # # # # # . . . . . . . . . .
. . . . . # # # # # . . . . . . . . . . . . . . .
# # # # # . . . . . . . . . . . . . . . . . . . .
`)
let state = "SAFE"
let averagedValue = 0
while (!input.buttonIsPressed(Button.A)) {
let value = readSensor()
// Apply some 'lag' filtering to the value, so that 'bobbing' doesn't set off alarm
// i.e. to go from 0 to 4 takes 4 times round the loop
if (value > averagedValue) {
// On the way up, changes in reading are slowed down
averagedValue = averagedValue + 1
} else if (value < averagedValue) {
// On the way down, changes in reading happen immediately
averagedValue = value
}
// Work out what to do based on the averaged value (non bobbing value)
if (state == "SAFE") {
if (averagedValue >= 4) {
pins.digitalWritePin(DigitalPin.P1, 1)
state = "ALARMING"
} else {
// Display raw value as a line
line.showImage(value * 5)
// fill in based on averaged value (long term reading)
for (let i = 0; i < averagedValue; i++) {
led.plot(2, 4 - i)
}
}
} else if (state == "ALARMING") {
if (input.buttonIsPressed(Button.B)) {
pins.digitalWritePin(DigitalPin.P1, 0)
state = "SAFE"
} else {
basic.showAnimation(`
# # # # # . . . . .
# # # # # . . . . .
# # # # # . . . . .
# # # # # . . . . .
# # # # # . . . . .
`, 400)
}
}
basic.pause(300)
}
waitNoButtons()
}
/**
* Read a value 0,1,2,3,4 from either ANALOG or TILT
*/
function readSensor(): number {
let value = 0
// Produce a sensor value in range 0..1023
if (sensorType == "ANALOG") {
// Input range is 0..1023
value = pins.analogReadPin(AnalogPin.P0)
} else if (sensorType == "TILT") {
// Input range is -1024 (pads highest)..1023 (pads lowest)
value = input.acceleration(Dimension.Y)
// Emulator sometimes returns out of range values, so clamp to expected range
value = Math.clamp(-1024, 1023, value)
// Convert to an entirely positive range from 0 pads full up to 2047 pads full down
// (hinged at the 'eyes' end)
value = value + 1024
// Invert the direction, so that "pads up" is largest, "pads down" is smallest
value = 2047 - value
// We want the same range as the analog, so halve the range
value = value / 2
}
// 5 possible values (0,1,2,3,4) based on sensible thresholds
// We do this by thresholds, so that we have more control over the 5 levels
if (value < 200) {
return 0
} else if (value < 400) {
return 1
} else if (value < 600) {
return 2
} else if (value < 800) {
return 3
}
return 4
}
function waitNoButtons() {
while (input.buttonIsPressed(Button.A) || input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
}

529
tests/wg-operation.ts Normal file
View File

@ -0,0 +1,529 @@
let opMaxTime: number
let procMaxTime: number
let procsToDo: number
let recoveryTriesMax: number
let tenseTime: number
let flatlineTimeMax: number
let recoveryProbability: number
let aWasPressed: boolean
let bWasPressed: boolean
let digitsImg: Image
let tweezerCount_: number
let wasTweezers: boolean
let wasNose: boolean
let isTweezers: boolean
// P0 blue wire, tweezers (active high)
// P1, green wire nose button/LED (active high)
// P2/red speaker (not a self toned beeper, but a piezo or a speaker)
opMaxTime = 120 * 1000
procsToDo = 3
procMaxTime = 60 * 1000
tenseTime = 4000
flatlineTimeMax = 15 * 1000
recoveryTriesMax = 3
recoveryProbability = 60
let highScore = 0
digitsImg = images.createImage(`
. . # . . . . # . . . # # . . . # # . . . # . . . . # # # . . . # # . . # # # . . . # . . . . # . .
. # . # . . # # . . . . . # . . . . # . . # . . . . # . . . . # . . . . . . . . . # . # . . # . # .
. # . # . . . # . . . . # . . . . # . . . # # # . . . # . . . # # . . . . # . . . . # . . . . # # .
. # . # . . . # . . . # . . . . . . # . . . # . . . . . # . . # . # . . # . . . . # . # . . . . # .
. . # . . . # # # . . # # # . . # # . . . . # . . . # # . . . . # . . . # . . . . . # . . . # # . .
`)
startIOMonitor()
while (true) {
splashScreen()
if (buttonB()) {
basic.showAnimation(`
. . . . . # # . . . . # . . . . # . . . . . . # . . . . . .
. . . . # . . . . # # # . . # . # . . # . . # . # . . . . #
. . . . # . . . . # . . . . # # # . . # # # . . # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# . . . # # . . . # # . . . # # . . . # # . . . # # . . . #
`, 400)
let finalScore = surgery()
if (game.score() > highScore) {
basic.showString("HI", 150)
highScore = finalScore
}
flashDigit(finalScore)
waitButtonB()
} else if (buttonA()) {
testMode()
} else {
basic.pause(100)
}
}
function surgery(): number {
let score = 0
let speed = 150
let opStartTime = input.runningTime()
let procStartTime = -1
let procNumber = -1
let procsDone = 0
let recoveryTry = 0
let timer = 0
let state = "CHOOSE PROC"
resetButtons()
while (true) {
basic.pause(10)
let event = getEvent(state)
if (event == "CUT") {
state = "ARREST"
}
// CHECK TIMERS
if (!(procStartTime == -1)) {
if (input.runningTime() - procStartTime > procMaxTime) {
state = "LOST"
} else {
// TODO add code here to speed up near end of proc
}
}
if (input.runningTime() - opStartTime > opMaxTime) {
state = "LOST"
} else {
// TODO add code here to speed up near end of op
}
// PROCESS SPECIFIC STATES
if (state == "CHOOSE PROC") {
if (procNumber == -1) {
procNumber = 1
showDigit(procNumber)
} else if (event == "SCROLL") {
procNumber = procNumber + 1
if (procNumber > 9) {
procNumber = 1
}
showDigit(procNumber)
} else if (event == "SELECT") {
procStartTime = input.runningTime()
state = "HEALTHY"
speed = 100
}
} else if (state == "HEALTHY") {
speed = 100
ecg(speed)
if (event == "TWINGE") {
state = "TENSE"
timer = input.runningTime()
} else if (event == "DONE") {
state = "PROC DONE"
}
} else if (state == "TENSE") {
speed = 25
ecg(speed)
if (event == "TWINGE") {
state = "ARREST"
} else if (input.runningTime() - timer > tenseTime) {
state = "HEALTHY"
}
} else if (state == "ARREST") {
timer = input.runningTime()
recoveryTry = recoveryTriesMax
state = "FLATLINE"
} else if (state == "FLATLINE") {
basic.showLeds(`
. . . . .
. # . # .
. . . . .
. . . . .
# # # # #
`)
beepOn()
if (event == "SHOCK") {
state = "SHOCKING"
} else if (input.runningTime() - timer > flatlineTimeMax) {
state = "LOST"
}
} else if (state == "SHOCKING") {
charging()
basic.showAnimation(`
. . . . # . . . . # . . . . . . . . . .
. . . # . . . . # . . . . # . . . . . .
. . . . . . . # . . . . # . . . . # . .
. . . . . . . . . . . # . . . . # . . .
. . . . . . . . . . . . . . . . # . . .
`, 150)
beepNTimesFor(15, 500)
basic.showAnimation(`
. . . . . . . . . . . . . . . # . . . #
. . . . . . . . . . # . . # . . . . . .
. . . . . # . # . . . . . . . . . . . .
. # . . . . # . . . . . . . . . . . . .
. # . . . . # . . . . # . . . . # . . .
`, 150)
state = "SHOCKED"
} else if (state == "SHOCKED") {
let recover = Math.random(100)
if (recover >= recoveryProbability) {
state = "RECOVERED"
} else {
state = "FAILED"
}
} else if (state == "RECOVERED") {
beepOff()
basic.pause(500)
beep()
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . . . .
. # . # . . . . . . . # . # . . . . . . . # . # .
. . . . . . . . . . . . . . . . . . . . . . . . .
# . . . # . . . . . # . . . # . . . . . # . . . #
. # # # . . . . . . . # # # . . . . . . . # # # .
`, 400)
state = "HEALTHY"
} else if (state == "FAILED") {
recoveryTry = recoveryTry - 1
if (recoveryTry > 0) {
state = "FLATLINE"
} else {
state = "LOST"
}
} else if (state == "PROC DONE") {
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . #
. . . . . . . . . . . . . . . . . . # . . . . # .
# . . . . # . . . . # . # . . # . # . . # . # . .
. . . . . . # . . . . # . . . . # . . . . # . . .
`, 400)
score = score + pointsForProc(procNumber)
procsDone = procsDone + 1
procStartTime = -1
if (procsDone == procsToDo) {
state = "OP DONE"
} else {
procNumber = -1
state = "CHOOSE PROC"
}
} else if (state == "OP DONE") {
basic.showAnimation(`
. . . . . . . . # . . # . . . . . # . . . . . # . . . . . # . . # . . . . # . . . . . . . . . . . . . . . . .
. . . . # . . # . # . # . . # . . # . . . . . # . . . . . # . . # . . . . # . . . # . . . # . . . . . . . . .
# # # # # # # . . # # # . . # . # # . . . . # # . . . . # # . # # . . . . # . . . # . . . # . . . . . . . . .
# # # # # # # # # # # # # # # . # # # # . . # # # . . . # # . . . # # . . # # # . # . # # # . . # # . . . # #
# . . . # # . . . # # . . . # . # . . . . . # . . . . . # . . . . # . . . . # . . # . # . # . . # . . . . # .
`, 400)
return score
} else if (state == "LOST") {
beepOn()
basic.showLeds(`
. # . # .
. . # . .
. # . # .
. . . . .
# # # # #
`)
basic.pause(3000)
beepOff()
basic.showAnimation(`
. . . . . . . . . . # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . # # # # # # . . . . # . . . . # . . . . # # . # . # . . . . # # . # . # . . . . #
# # # # # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# . . . # # . . . # # . . . # # . . . # # . . . # # . . . # # . . . # # . . . # # . . . #
`, 400)
return score
}
}
}
/**
* if any button pressed, terminate the animation immediately
*/
function splashScreen() {
let img = images.createImage(`
. # . # . . # . # . . # . # . . # . # . . . # . . . # # . .
# # # # # # . # . # # # # # # # . # . # . # . # . . # . # .
# # # # # # . . . # # # # # # # . . . # . # . # . . # # . .
. # # # . . # . # . . # # # . . # . # . . # . # . . # . . .
. . # . . . . # . . . . # . . . . # . . . . # . . . # . . .
`)
let x = 0
while (!aWasPressed && !bWasPressed) {
img.showImage(x)
basic.pause(500)
x = x + 5
if (x >= img.width()) {
x = 0
}
}
}
/**
* Test sensing and buzzing
* I/O at moment is (assuming self toning beeper)
* P0 is beeper and nose LED
* P1 is the tweezer sense wire
* P2 is possibly the nose button
* If we want amplification, might have to use piezo library
* which means using extra pins
*/
function testMode() {
while (!(buttonA())) {
if (pins.digitalReadPin(DigitalPin.P1) == 1) {
pins.digitalWritePin(DigitalPin.P0, 1)
basic.showLeds(`
. . . . .
. . . . #
. . . # .
# . # . .
. # . . .
`)
} else {
pins.digitalWritePin(DigitalPin.P0, 0)
basic.showLeds(`
# # # # #
. . # . .
. . # . .
. . # . .
. . # . .
`)
}
basic.pause(100)
}
}
/**
* SENSE TWINGE/CUT FROM TWEEZERS (10ms per sample)
* @param state TODO
*/
function getEvent(state: string): string {
if (wasTweezers) {
if (tweezerCount_ > 20) {
wasTweezers = false
tweezerCount_ = 0
return "CUT"
} else if (!isTweezers) {
wasTweezers = false
tweezerCount_ = 0
return "TWINGE"
}
}
// SENSE A OR B BUTTON PRESSES
if (state == "CHOOSE PROC") {
if (buttonA()) {
return "SCROLL"
} else if (buttonB()) {
return "SELECT"
}
} else if (state == "FLATLINE") {
if (buttonB()) {
return "SHOCK"
} else if (nose()) {
return "SHOCK"
}
} else if (state == "HEALTHY") {
if (buttonB()) {
return "DONE"
}
}
// Clear any flags for unnecessary events in a given state to prevent latching
aWasPressed = false
bWasPressed = false
return "NONE"
}
function flashDigit(digit: number) {
for (let i = 0; i < 4; i++) {
showDigit(digit)
basic.pause(200)
basic.clearScreen()
basic.pause(200)
}
showDigit(digit)
basic.pause(1000)
}
/**
* Make a short beep sound
*/
function beep() {
beepOn()
basic.pause(200)
beepOff()
}
/**
* work out score for a procedure
* @param procNumber TODO
*/
function pointsForProc(procNumber: number): number {
if (procNumber < 4) {
return 1
} else if (procNumber < 7) {
return 2
} else {
return 3
}
}
/**
* beep n times, for a total duration of m
* @param times TODO
* @param duration TODO
*/
function beepNTimesFor(times: number, duration: number) {
let halfCycle = duration / (times * 2)
for (let i = 0; i < times; i++) {
beepOn()
basic.pause(halfCycle)
beepOff()
basic.pause(halfCycle)
}
}
function startIOMonitor() {
aWasPressed = false
input.onButtonPressed(Button.A, () => {
aWasPressed = true
})
bWasPressed = false
input.onButtonPressed(Button.B, () => {
bWasPressed = true
})
wasTweezers = false
isTweezers = false
tweezerCount_ = 0
control.inBackground(() => {
let buzzCount = 0
while (true) {
if (pins.digitalReadPin(DigitalPin.P0) == 1) {
wasTweezers = true
isTweezers = true
tweezerCount_ = tweezerCount_ + 1
if (buzzCount == 0) {
pins.analogWritePin(AnalogPin.P2, 512)
pins.analogSetPeriod(AnalogPin.P2, 5000)
}
buzzCount = 10
} else {
isTweezers = false
}
if (buzzCount > 0) {
buzzCount = buzzCount - 1
if (buzzCount == 0) {
pins.analogWritePin(AnalogPin.P2, 0)
}
}
basic.pause(10)
}
})
}
/**
* Shows a single digit, with a nicer font than the standard micro:bit font
* @param digit TODO
*/
function showDigit(digit: number) {
digit = Math.clamp(0, 9, digit)
digitsImg.showImage(digit * 5)
}
/**
* check to see if button A was pressed recently
*/
function buttonA(): boolean {
if (aWasPressed) {
aWasPressed = false
return true
}
return false
}
function waitButtonA() {
while (!(buttonA())) {
basic.pause(100)
}
}
function buttonB(): boolean {
if (bWasPressed) {
bWasPressed = false
return true
}
return false
}
function waitButtonB() {
while (!(buttonB())) {
basic.pause(100)
}
}
function beepOn() {
pins.analogWritePin(AnalogPin.P2, 512)
pins.analogSetPeriod(AnalogPin.P2, 2272)
}
function beepOff() {
pins.analogWritePin(AnalogPin.P2, 0)
}
function resetButtons() {
aWasPressed = false
bWasPressed = false
}
function ecg(speed: number) {
beepOn()
pins.digitalWritePin(DigitalPin.P1, 1)
basic.pause(50)
beepOff()
basic.showAnimation(`
. . . . # . . . # . . . # . . . # . . . # . . . . . . . . . . . . . .
. . . . # . . . # . . . # . . . # . . . # . . . . . . . . . . . . . .
. . . . # . . . # . . . # . . . # . . . # . . . . . . . . . . . . . .
. . . . # . . . # # . . # # . . # # . . # # . . . # . . . . . . . . .
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
`, speed)
pins.digitalWritePin(DigitalPin.P1, 0)
basic.pause(speed * 10)
}
function nose(): boolean {
if (wasNose) {
wasNose = false
return true
}
return false
}
/**
* start period in microseconds
*/
function charging() {
let period = 2000
let dec = 500
pins.analogWritePin(AnalogPin.P2, 512)
pins.analogSetPeriod(AnalogPin.P2, period)
basic.showLeds(`
. # # . .
. . . # .
. . # . .
. . . # .
. # # . .
`)
basic.pause(500)
pins.analogSetPeriod(AnalogPin.P2, period - dec)
basic.showLeds(`
. # # . .
. . . # .
. . # . .
. # . . .
. # # # .
`)
basic.pause(500)
pins.analogSetPeriod(AnalogPin.P2, period - dec * 2)
basic.showLeds(`
. . # . .
. # # . .
. . # . .
. . # . .
. # # # .
`)
basic.pause(500)
beepOff()
}

View File

@ -0,0 +1,178 @@
let wasShake: boolean
input.onShake(() => {
wasShake = true
})
wait_6Hours()
timeForTest()
alertnessTest()
wait_2Hours()
timeForTest()
alertnessTest()
wait_2Hours()
timeForTest()
alertnessTest()
/**
* animate back to work
* animate take a rest
*/
function animations() { }
/**
* wait 6 hours
* because this is a test program, we only wait for a short time
*/
function wait_6Hours() {
basic.showAnimation(`
. . # . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . # . . . .
. . # . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . .
. . # . . . . # . . . . # # # . . # . . . . # . . . . # . . # # # . . . . # . .
. . . . . . . . . . . . . . . . . . # . . . # . . . # . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . # . . # . . # . . . . . . . . . . . . . .
`, 500)
}
/**
* wait for 2 hours
* because this is test code, we only wait a few seconds
*/
function wait_2Hours() {
basic.showAnimation(`
. . # . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . # . . . .
. . # . . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . .
. . # . . . . # . . . . # # # . . # . . . . # . . . . # . . # # # . . . . # . .
. . . . . . . . . . . . . . . . . . # . . . # . . . # . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . # . . # . . # . . . . . . . . . . . . . .
`, 500)
}
function alertnessTest() {
let goodResponse = 1000
let threshold = 5
let score = 0
// Start test on button press
let x = 0
let start = images.createImage(`
. . # . . . . # . .
. . # . . . # . # .
. . # . . . . . # .
. . . . . . . # . .
. . # . . . . # . .
`)
while (!input.buttonIsPressed(Button.B)) {
start.showImage(x)
x = x + 5
if (x >= start.width()) {
x = 0
}
basic.pause(300)
}
// Wait for button(s) to be released
while (input.buttonIsPressed(Button.A) || input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
// Run 10 random cognition response tests
for (let i = 0; i < 9; i++) {
// Choose random delay and random outcome
let delay = Math.random(5) + 5
let outcome = Math.random(2)
// Draw moving dots on screen until delay expires
basic.clearScreen()
for (let j = 0; j < delay; j++) {
led.plot(j % 5, 2)
basic.pause(200)
basic.clearScreen()
}
// Show shake or button icon
if (outcome == 0) {
// Press the button!
basic.showLeds(`
. . . . .
. # # # .
. # # # .
. # # # .
. . . . .
`)
} else {
// Shake the bit!
basic.showLeds(`
# . # . .
# . . # .
# # # # #
# . . # .
# . # . .
`)
}
// Wait up to 3 seconds for button, shake, or timeout
wasShake = false
let timer = input.runningTime()
let timeout = 3000
while (input.runningTime() < timer + timeout) {
if (wasShake) {
break
} else if (input.buttonIsPressed(Button.B)) {
break
} else {
basic.pause(100)
}
}
// Assess the response and the response time
let response = input.runningTime() - timer
if (outcome == 0 && input.buttonIsPressed(Button.B) && response <= goodResponse) {
score = score + 1
} else if (outcome == 1 && wasShake && response <= goodResponse) {
score = score + 1
}
}
// Show final score flashing 5 times (0..9)
for (let k = 0; k < 5; k++) {
basic.showNumber(score, 0)
basic.pause(250)
basic.clearScreen()
basic.pause(250)
}
basic.showNumber(score, 0)
basic.pause(500)
if (score < threshold) {
// Time for a break, show coffee cup animation
for (let l = 0; l < 3; l++) {
basic.showAnimation(`
. . . . . . . . . . . # . . . . # . . . . . . . . . . # . . . . # . . . . . . .
. . . . . . # . . . . # . . . . . . . . . . # . . . . # . . . . . . . . . . . .
# . . # . # # . # . # . . # . # . . # . # . # # . # . . # . # . . # . # . . # .
# . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # #
# # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # .
`, 400)
}
} else {
// All ok, back to work, show digging animation
for (let m = 0; m < 3; m++) {
basic.showAnimation(`
# . # . . # . . . . # . . . .
. # # . . . # . # . . # . . .
# # # . . . . # # . . . # . #
. . . . . . # # # . . . . # #
. . . . . . . . . . . . # # #
`, 400)
}
}
// Wait for any button press to finish test
while (!input.buttonIsPressed(Button.A) && !input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
}
/**
* alert the user it is time to take the test
* in a real system, this might give them 5 1 minute warnings
*/
function timeForTest() {
basic.showAnimation(`
. # # # . . # . . . . # # . . . # # . . . . # . .
. # . . . . # . . . . . . # . . . . # . . # # . .
. . # . . . # # # . . # # . . . . # . . . . # . .
. . . # . . . # . . . . . # . . # . . . . . # . .
. # # . . . . # . . . # # . . . # # # . . # # # .
`, 700)
}

157
tests/wg-paintroller.ts Normal file
View File

@ -0,0 +1,157 @@
let maxGameTime: number
let wasShake: boolean
maxGameTime = 60000
input.onShake(() => {
wasShake = true
})
while (true) {
splashScreen()
if (input.buttonIsPressed(Button.B)) {
basic.showAnimation(`
# # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # . . # # # # #
# # # . . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # . . # . . . . # . . . . # . . . . # . . . . # # # # # # # # # #
# # # . . # # # . . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # . . # # # . . # # # . . # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # # . . # # # . # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
`, 400)
let score = playOneGame()
flashDigit(score, 5)
waitButtonB()
basic.showAnimation(`
# # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# # # # # # # # # # . . . . . . . . . . . . . . . . . . . . . # . # . . . . . . . # . # . . . . . .
# # # # # # # # # # # # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# # # # # # # # # # # # # # # # # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# # # # # # # # # # # # # # # # # # # # # # # # # . . . . . . . . . . . . . . . . . . . . . . . . .
`, 400)
} else if (input.buttonIsPressed(Button.A)) {
calibrate()
}
}
function playOneGame(): number {
let countDots = 0
let x = 2
let y = 2
let xdir = true
let canvas = images.createImage(`
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
`)
wasShake = false
let bearing = input.compassHeading()
let startTime = input.runningTime()
while (countDots < 25 && input.runningTime() - startTime < maxGameTime) {
if (wasShake) {
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . # # # . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . # # # . . . # . . . # # # . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . # # # . . . # . . . . # . . . . # . . . # # # . . . . . . . . . . . . . . . .
. . . . . . # # # . . . # . . . . # . . . . # . . . . # . . . . # . . . # # # . . . . . . . . . . .
. # # # . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . # # # . . . . . .
`, 50)
let pos = Math.random(5)
if (xdir) {
if (!canvas.pixel(pos, y)) {
canvas.setPixel(pos, y, true)
countDots = countDots + 1
}
} else if (!canvas.pixel(x, pos)) {
canvas.setPixel(x, pos, true)
countDots = countDots + 1
}
wasShake = false
canvas.showImage(0)
} else if (Math.abs(input.compassHeading() - bearing) > 60) {
xdir = !xdir
bearing = input.compassHeading()
if (xdir) {
x = Math.random(5)
} else {
y = Math.random(5)
}
} else {
basic.pause(100)
}
}
return dotsToScore(countDots)
}
function splashScreen() {
let img = images.createImage(`
# # # # . # # # # . # # # # . # # # # . # # # # . # # # # . . # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . # # . . # # # . # # # # . # # # # . . # # # . . . # # . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . # # # # . # # # # . # # # # . # # # # . # # # # .
# . . . # # . . . # # . . . # # . . . # # . . . # . # # # . . . # . . . # # # . . . . . . . . . . . . . . . . . . . . . # . . . . . # . . . . . # . . . . . # . . . . . # . . . # # . . # . # . # . . # # . . . # # . . . # # . . . # # . . . # . # . . # . . # . # . . . # # . . . . # . . . # . . . # . . . # . . . # . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . # . . . # # # . # . . . # # . . . # # . . . # # . . . #
# # # # . # # # # . # # # # . # # # # . . # # # . . . # . . . . # . . . . # . . . # # # . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # . # # # # # # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # . # # # # # # # # # . # # # . . # # . . . # . . . . . . . . . . . . . . . . . . . . # # # . . . # . . . . # . . . . # . . . # # # . # # # # . # # # # . # # # # .
# . . . . # . . . . # . . . . . # # # . . . # . . . . # . . . . # . . . . # . . . . # . . . # # # . . . . . . . . . . . # . . . . . # . . . . . # . . . . . # . . . . . # . . . # . . . # # . . # . # . # . . # . # . . # . # . . # . # . . # . . # . # . . . # # . . . . # . . . . . # . . . # . . . # . . . # . . . # . . . . . . . . . . . . . . . # # # . . . # . . . . # . . . . # . . . . # . . . . # . . . # # # . # . . . . # . . . .
# . . . . # . . . . . # # # . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # . . . . # . . . . # # . . . # # . . . # . . . . # . . . . # . . . . # . . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . # # # . # . . . .
`)
let x = 0
while (!input.buttonIsPressed(Button.A) && !input.buttonIsPressed(Button.B)) {
img.showImage(x)
basic.pause(200)
x = x + 5
if (x >= img.width()) {
x = 0
}
}
}
/**
* rotate canvas
*/
function rotateImage() { }
function flashDigit(digit: number, times: number) {
for (let i = 0; i < times; i++) {
basic.showNumber(digit, 0)
basic.pause(500)
basic.clearScreen()
basic.pause(500)
}
basic.showNumber(digit, 0)
}
function waitButtonB() {
while (!input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
}
function dotsToScore(dots: number): number {
if (dots == 25) {
return 9
} else if (dots >= 22) {
return 8
} else if (dots >= 19) {
return 7
} else if (dots >= 16) {
return 6
} else if (dots >= 13) {
return 5
} else if (dots >= 10) {
return 4
} else if (dots >= 7) {
return 3
} else if (dots >= 4) {
return 2
} else if (dots >= 1) {
return 1
} else {
return 0
}
}
function calibrate() {
basic.showString("CAL", 150)
if (input.compassHeading() == -4) {
input.calibrate()
}
while (!input.buttonIsPressed(Button.B)) {
let h = input.compassHeading()
basic.showNumber(h, 150)
}
}

View File

@ -0,0 +1,267 @@
let state: string
let timer: number
let gesturesRunning: boolean
let wasShake: boolean
let wasLogoUp: boolean
let wasLogoDown: boolean
let wasScreenUp: boolean
let wasScreenDown: boolean
while (true) {
splashScreen()
if (input.buttonIsPressed(Button.B)) {
// animate: add pancake
basic.showAnimation(`
# # # # # # . . . # . . . . . . . . . . . . . . .
. . . . . . # # # . . # # # . . . . . . . . . . .
. . . . . . . . . . # . . . # # . . . # . . . . .
. . . . . . . . . . . . . . . . # # # . . # # # .
. . . . . . . . . . . . . . . . . . . . # . . . #
`, 250)
runGameOnce()
// animate: pancake done
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # . # # # # # . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . # . . . # # # # # # # . . . # . . . . . . . . . . . # . # . . . . . . . # . # . . . . . .
. . . . . . . . . . . # # # . # # # # # . # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# . . . # # # # # # # . . . # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
`, 250)
} else if (input.buttonIsPressed(Button.A)) {
testShake()
}
}
/**
* Runs one complete game from start to end
*/
function runGameOnce() {
let score = 0
let cooks = 0
let target_time = 0
// make sure no gestures are outstanding from last game
wasShake = false
wasLogoUp = false
wasLogoDown = false
wasScreenUp = false
wasScreenDown = false
state = "NEWSIDE"
while (true) {
// Handle any gestures that can happen at any time
let gesture = getGesture()
if (gesture == "FLOOR") {
state = "DROPPED"
}
// Run code appropriate to the present state of the game
if (state == "NEWSIDE") {
target_time = 5 + Math.random(5)
state = "COOKING"
startTimer()
} else if (state == "COOKING") {
// animate: cooking
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . # . . . . . . . . . . . . . #
# # # # # . # # # # # # # # # # # # # .
`, 100)
if (gesture == "FLIP") {
state = "FLIPPING"
} else if (getTimerSec() >= target_time) {
state = "READY"
score = score + 1
startTimer()
}
} else if (state == "READY") {
// animate: ready
basic.showAnimation(`
. . . . .
. . . . .
. . . . .
# . . . #
. # # # .
`, 100)
if (getTimerSec() > 2) {
state = "BURNING"
score = score - 1
startTimer()
} else if (gesture == "FLIP") {
state = "FLIPPING"
}
} else if (state == "BURNING") {
// animate: burning
basic.showAnimation(`
. . . . . . . . . . . . . # . . . . # .
. . . . . . . . # . . # . # . . # . . .
. . . . . . # . # . . # . . . . . . . .
. . . . . . # . . . . . . . . . . . . .
# # # # # # # # # # # # # # # # # # # #
`, 100)
if (gesture == "SKY") {
state = "NEWSIDE"
} else if (getTimerSec() > 2) {
state = "BURNT"
}
} else if (state == "FLIPPING") {
// animate: flipping
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . # . . . . . . . . . . . . . # . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . # # # # # . # # . . . # # # . . . # # . # # # # # . . . . . . . . . .
. . . . . . . . . . # # # # # . . . . . . . . # # . # # # . # # . . . . . . . . # # # # # . . . . .
. . . . . # # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # # # # #
# # # # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
`, 100)
// Prevent a spurious double-flip from happening
wasShake = false
cooks = cooks + 1
if (cooks == 5) {
state = "GAMEOVER"
} else {
state = "NEWSIDE"
}
} else if (state == "DROPPED") {
// animate: dropped
basic.showAnimation(`
# . . . # . . . . . # . . . # . . . . .
. # . # . . . . . . . # . # . . . . . .
. . # . . . . . . . . . # . . . . . . .
. # . # . . . . . . . # . # . . . . . .
# . . . # . . . . . # . . . # . . . . .
`, 250)
score = 0
state = "GAMEOVER"
} else if (state == "BURNT") {
// animate: burnt
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . # . . . . . . . . # . # . . . . . . . # # # .
. . . . . . . . . . . . . . . . . # . . . . . . . . # . # . . . . . . . # # # . . . . . .
. . . . . . . . . . . . . . . . . . . . . # . # . . . . . . . # # # . . . . . . . . . . .
. . . . . . # . # . . # # # . . # . # . . . . . . . # # # . . . . . . . . . . . . . . . .
# # # # # . # # # . . # # # . . # # # . . # # # . . . . . . . . . . . . . . . . . . . . .
`, 250)
score = 0
state = "GAMEOVER"
} else if (state == "GAMEOVER") {
animateScore(score)
state = "WAITEJECT"
} else if (state == "WAITEJECT") {
if (gesture == "UPSIDEDOWN") {
return
}
}
}
}
/**
* show score (0..9) flashing
* @param score TODO
*/
function animateScore(score: number) {
score = Math.clamp(0, 9, score)
for (let i = 0; i < 5; i++) {
basic.showNumber(score, 0)
basic.pause(500)
basic.clearScreen()
basic.pause(500)
}
basic.showNumber(score, 0)
}
/**
* NOTE: Eventually this will move into a gesture library
* It hides all the nasty detail of detecting gestures
*/
function getGesture(): string {
if (!gesturesRunning) {
input.onShake(() => {
wasShake = true
})
input.onLogoUp(() => {
wasLogoUp = true
})
input.onLogoDown(() => {
wasLogoDown = true
})
input.onScreenUp(() => {
wasScreenUp = true
})
input.onScreenDown(() => {
wasScreenDown = true
})
gesturesRunning = true
}
// work out which of a possible set of gestures has occurred:
// Button gestures and movement gestures both handled
// This is so that it is easy to also use this on the simulator too
// Generally, B is used for normal OK, A is used for abnormal RECOVERY
// (flip is a normal action, touch sky to turn off smoke alarm is recovery)
let a = input.buttonIsPressed(Button.A)
let b = input.buttonIsPressed(Button.B)
if (state == "COOKING" || state == "READY") {
if (b || wasShake) {
wasShake = false
return "FLIP"
}
} else if (state == "FLIPPING") {
if (a || wasLogoDown) {
wasLogoDown = false
return "FLOOR"
}
} else if (state == "BURNING") {
if (a || wasLogoUp) {
wasLogoUp = false
return "SKY"
}
} else if (state == "GAMEOVER" || state == "WAITEJECT") {
if (b || wasScreenDown) {
wasScreenDown = false
return "UPSIDEDOWN"
}
}
return "NONE"
}
/**
* start timer by sampling runningtime and storing into starttime
*/
function startTimer() {
timer = input.runningTime()
}
/**
* get the elapsed time from the global starttime with ref to running time
* in seconds.
*/
function getTimerSec(): number {
let t = (input.runningTime() - timer) / 1000
return t
}
/**
* Show a splash screen "Perfect Pancakes >>>"
* Splash screen "PP" with little arrow pointing to the start button
*/
function splashScreen() {
let splash = images.createImage(`
# # # # . . . . . . # # # # . . . . . . . . . . . . . . . . . . . . .
# . . . # . . . . . # . . . # # . . . . . # . . . . . # . . . . . # .
# # # # . . . . . . # # # # . . # . . . . . # . . . . . # . . . . . #
# . . . . . . . . . # . . . . # . . . . . # . . . . . # . . . . . # .
# . . . . . . . . . # . . . . . . . . . . . . . . . . . . . . . . . .
`)
// use show image (not show animation) so that button press is more responsive
let index = 0
// Any button press finishes the splash screen
while (!input.buttonIsPressed(Button.B) && !input.buttonIsPressed(Button.A)) {
splash.showImage(index * 5)
index = index + 1
if (index > splash.width() / 5) {
index = 0
}
basic.pause(250)
}
}
function testShake() { }

242
tests/wg-right-sugar.ts Normal file
View File

@ -0,0 +1,242 @@
let sugarThreshold: number
let ketoneThreshold: number
// Important parameters
sugarThreshold = 14
ketoneThreshold = 6
while (true) {
// splash screen sugar cube (just the right sugar)
basic.showAnimation(`
# # # . . . . . . . . . . . . . . . . .
# # # . . . # # # . . . . . . . . . . .
# # # . . . # # # . . . # # # . . # # #
. . . . . . # # # . . . # # # . . # # #
. . . . . . . . . . . . # # # . . # # #
`, 400)
// ask questions and give advice
quiz()
// wait for button press before restart
waitAnyButton()
}
function getSugar(): string {
waitNoButtons()
let selection = "MID"
while (!input.buttonIsPressed(Button.B)) {
// high, low, normal?
basic.showAnimation(`
. . . . . # . # . . . . # . .
. # # # . # . # . . . # . # .
. # # # . # . # # # . . . # .
. # # # . # . . # . . . # . .
. . . . . # . . # . . . # . .
`, 400)
// show low, mid, or high as a bar
selection = getLowMidHigh(selection)
}
return selection
}
function getKetone(): string {
waitNoButtons()
let selection = "MID"
while (!input.buttonIsPressed(Button.B)) {
// high, low, normal?
basic.showAnimation(`
. . . . . . . . # . . . # . .
# # . . . . . # . . . # . # .
# # # # # . . # # . . . . # .
# # . . # . . # . # . . # . .
. . . . . # . . # . . . # . .
`, 400)
// show low, mid, or high as a bar
selection = getLowMidHigh(selection)
}
return selection
}
function getIll(): boolean {
waitNoButtons()
let selection = false
while (!input.buttonIsPressed(Button.B)) {
// ask question 'happy or sad'
basic.showAnimation(`
. . . . . . . . . . . . # . . . . # . .
. # . # . . # . # . . # . # . . # . # .
. . . . . . . . . . . . . # . . . . # .
# . . . # . # # # . . . # . . . . # . .
. # # # . # . . . # . . # . . . . # . .
`, 400)
// get answer from user
selection = getHappySad(selection)
}
// if we are happy, we are not ill
return !selection
}
function getLowMidHigh(selection: string): string {
let timeout = 2000
let timer = input.runningTime()
while (true) {
// show the present level as a line
if (selection == "LOW") {
basic.showLeds(`
. . . . .
. . . . .
. . . . .
. . . . .
# # # # #
`)
} else if (selection == "HIGH") {
basic.showLeds(`
# # # # #
. . . . .
. . . . .
. . . . .
. . . . .
`)
} else {
basic.showLeds(`
. . . . .
. . . . .
# # # # #
. . . . .
. . . . .
`)
}
// process any user input
if (input.buttonIsPressed(Button.A)) {
// cycle round the 3 possible levels
if (selection == "LOW") {
selection = "MID"
} else if (selection == "MID") {
selection = "HIGH"
} else {
selection = "LOW"
}
// This is the 'hold repeat' time if you hold the A button
basic.pause(300)
// restart timer, 2 seconds of inactivity before return to main
timer = input.runningTime()
} else if (input.buttonIsPressed(Button.B)) {
// user is selecting so better return quickly
return selection
} else if (input.runningTime() > timer + timeout) {
// timeout, so return to main to re-prompt user
return selection
} else {
// This slows the loop down to stop the emulator being busy
// and also preserves battery life on the micro:bit
// it also affects the worst case response time
// it also affects the response time
basic.pause(100)
}
}
}
/**
* Wait for all buttons to be released
* This prevents spurious selection in next question
*/
function waitNoButtons() {
while (input.buttonIsPressed(Button.A) || input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
}
function getHappySad(selection: boolean): boolean {
let timeout = 2000
let timer = input.runningTime()
while (!input.buttonIsPressed(Button.B)) {
// Show present selection
if (selection) {
basic.showLeds(`
. . . . .
. # . # .
. . . . .
# . . . #
. # # # .
`)
} else {
basic.showLeds(`
. . . . .
. # . # .
. . . . .
. # # # .
# . . . #
`)
}
// Wait for any change in the selection
if (input.buttonIsPressed(Button.A)) {
selection = !selection
// This is the repeat time if button A held down
basic.pause(500)
timer = input.runningTime()
} else if (input.runningTime() > timer + timeout) {
return selection
} else {
// Preserve battery life on micro:bit and prevent emulator being busy
// This is also the response time to a button press
basic.pause(100)
}
}
return selection
}
/**
* Button A changes value, Button B selects value
*/
function quiz() {
let sugar = getSugar()
if (sugar != "HIGH") {
// All is ok (tick)
basic.showAnimation(`
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . #
. . . . . . . . . . . . . . . . . . # . . . . # .
# . . . . # . . . . # . # . . # . # . . # . # . .
. . . . . . # . . . . # . . . . # . . . . # . . .
`, 400)
} else {
// Button A changes value, Button B selects value
let ketone = getKetone()
if (ketone != "HIGH") {
// Button A changes value, Button B selects value
let ill = getIll()
if (!ill) {
// Time to rest (jump into bed)
basic.showAnimation(`
. . . . . # # . . . . # . . . . # . . . . . . # . . . . . .
. . . . # . . . . # # # . . # . # . . # . . # . # . . . . #
. . . . # . . . . # . . . . # # # . . # # # . . # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# . . . # # . . . # # . . . # # . . . # # . . . # # . . . #
`, 400)
} else {
// Test more often (clock shows 2 hour interval)
basic.showAnimation(`
. . # . . . . . . . . . . . . . . . . . . # # . . . . . . . . # # . . . . . . . . # # . .
. . # . . . . . . . . . . . . . . . . . . . . # . . . . . . . . . # . . . . . . . . . # .
. . # . . . . # # # . . # . . # # # . . . . # . . . . . . . . . # . . . . . . . . . # . .
. . . . . . . . . . . . # . . . . . . . . # . . . . . . . . . # . . . . . . . . . # . . .
. . . . . . . . . . . . # . . . . . . . . # # # . . . . . . . # # # . . . . . . . # # # .
`, 400)
}
} else {
// Get some help (call the diabetes care team on the phone)
basic.showAnimation(`
. . . . . . . . . . # # # # # # # . . . # # . . . # # . . . # # . . . # # # . . # # . # . # # . . #
. . . . . # # # # # # . . . # # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . .
# # # # # # . . . # . . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . .
# . # . # . . # . . . . # . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . # . . . .
. # # # . . # # # . . # # # . # # . . . # # . . # # # . # . # # # . . # # . . . # # . . . # # . . .
`, 400)
}
}
}
function waitAnyButton() {
while (!input.buttonIsPressed(Button.A) && !input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
}

488
tests/wg-user-confidance.ts Normal file
View File

@ -0,0 +1,488 @@
let AWasPressed: boolean
let BWasPressed: boolean
let wasShake: boolean
let dots025: Image
dots025 = images.createImage(`
. . . . . # . . . . # # . . . # # # . . # # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # . # # # # #
`)
let tests = "DABPXYZSCT"
let test = 0
let prevTest = -1
startIOMonitor()
while (true) {
let testLetter = tests[test]
let autoRun = false
if (testLetter == "D" || testLetter == "A" || testLetter == "B") {
autoRun = true
}
if (!(test == prevTest)) {
basic.showString(tests[test], 200)
prevTest = test
}
if (AWasPressed || autoRun) {
AWasPressed = false
if (testLetter == "D") {
testDisplay()
test = test + 1
} else if (testLetter == "A") {
testButtonA()
test = test + 1
} else if (testLetter == "B") {
testButtonB()
test = test + 1
} else if (testLetter == "P") {
testPads()
} else if (testLetter == "X") {
testTiltX()
} else if (testLetter == "Y") {
testTiltY()
} else if (testLetter == "Z") {
testTiltZ()
} else if (testLetter == "S") {
testShake()
} else if (testLetter == "C") {
testCompass()
} else if (testLetter == "T") {
testTemperature()
} else {
// end of tests
basic.showLeds(`
. . . . .
. . . . #
. . . # .
# . # . .
. # . . .
`, 400)
}
prevTest = -1
AWasPressed = false
BWasPressed = false
} else if (BWasPressed) {
BWasPressed = false
if (test < tests.length - 1) {
test = test + 1
} else {
test = 3
}
} else {
basic.pause(100)
}
}
/**
* flash all LEDs 5 times
*/
function testDisplay() {
for (let i = 0; i < 5; i++) {
basic.plotLeds(`
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
`)
basic.pause(200)
basic.clearScreen()
basic.pause(200)
}
// cycle all LEDs from 1 to 25
basic.showAnimation(`
# . . . . # # . . . # # # . . # # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # . # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . # . . . . # # . . . # # # . . # # # # . # # # # #
`, 400)
}
function testButtonA() {
basic.plotLeds(`
. . # . .
. # . # .
. # # # .
. # . # .
. # . # .
`)
// wait for A pressed
while (!input.buttonIsPressed(Button.A)) {
basic.pause(100)
}
basic.plotLeds(`
. # . . .
# . # . .
# # # . .
# . # . .
# . # . .
`)
// wait for A released
while (input.buttonIsPressed(Button.A)) {
basic.pause(100)
}
basic.plotLeds(`
. . # . .
. # . # .
. # # # .
. # . # .
. # . # .
`)
basic.pause(1000)
}
function testTiltX() {
basic.clearScreen()
let prevx = 0
while (!AWasPressed && !BWasPressed) {
basic.pause(100)
let x = input.acceleration(Dimension.X)
let x2 = x / 512 + 2
let x3 = Math.clamp(0, 4, x2)
// sticky trace
led.plot(x3, 0)
// middle line is actual/live
if (x3 != prevx) {
led.unplot(prevx, 2)
prevx = x3
}
led.plot(x3, 2)
// bottom line is -4G, -2G, 1G, +2G, +4G
if (x <= -2048) {
led.plot(0, 4)
} else if (x <= -1024) {
led.plot(1, 4)
} else if (x <= 1024) {
led.plot(2, 4)
} else if (x <= 2048) {
led.plot(3, 4)
} else {
led.plot(4, 4)
}
}
}
function testShake() {
wasShake = false
basic.plotLeds(`
. . . . .
. . . . .
. . # . .
. . . . .
. . . . .
`)
while (!AWasPressed && !BWasPressed) {
if (wasShake) {
wasShake = false
basic.plotLeds(`
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
`)
basic.pause(500)
basic.plotLeds(`
. . . . .
. . . . .
. . # . .
. . . . .
. . . . .
`)
} else {
basic.pause(100)
}
}
}
function testCompass() {
if (input.compassHeading() < 0) {
input.calibrate()
}
basic.clearScreen()
while (!AWasPressed && !BWasPressed) {
let d = input.compassHeading()
d = d / 22
d = Math.clamp(0, 15, d)
d = (d + 2) % 16
if (d < 4) {
led.plot(d, 0)
} else if (d < 8) {
led.plot(4, d - 4)
} else if (d < 12) {
led.plot(4 - d - 8, 4)
} else {
led.plot(0, 4 - d - 12)
}
basic.pause(100)
}
}
function testPads() {
let TESTSPEED = 500
AWasPressed = false
BWasPressed = false
// Make sure all pins are inputs, before test starts
let p0 = pins.digitalReadPin(DigitalPin.P0)
let p1 = pins.digitalReadPin(DigitalPin.P1)
let p2 = pins.digitalReadPin(DigitalPin.P2)
let ok0 = 0
let ok1 = 0
let ok2 = 0
while (!AWasPressed && !BWasPressed) {
basic.clearScreen()
// ## P0 out low, read from P1 and P2
ok0 = 0
pins.digitalWritePin(DigitalPin.P0, 0)
basic.pause(TESTSPEED)
p1 = pins.digitalReadPin(DigitalPin.P1)
p2 = pins.digitalReadPin(DigitalPin.P2)
if (p1 == 0) {
led.plot(0, 0)
ok0 = ok0 + 1
}
if (p2 == 0) {
led.plot(1, 0)
ok0 = ok0 + 1
}
// ## P0 out high, read from P1 and P2
pins.digitalWritePin(DigitalPin.P0, 1)
basic.pause(TESTSPEED)
p1 = pins.digitalReadPin(DigitalPin.P1)
p2 = pins.digitalReadPin(DigitalPin.P2)
if (p1 == 1) {
led.plot(2, 0)
ok0 = ok0 + 1
}
if (p2 == 1) {
led.plot(3, 0)
ok0 = ok0 + 1
}
// set back to an input
p0 = pins.digitalReadPin(DigitalPin.P0)
// ## P1 out low, read from P0 and P2
ok1 = 0
pins.digitalWritePin(DigitalPin.P1, 0)
basic.pause(TESTSPEED)
p0 = pins.digitalReadPin(DigitalPin.P0)
p2 = pins.digitalReadPin(DigitalPin.P2)
if (p0 == 0) {
led.plot(0, 1)
ok1 = ok1 + 1
}
if (p2 == 0) {
led.plot(1, 1)
ok1 = ok1 + 1
}
// ## P1 out high, read from P0 and P2
pins.digitalWritePin(DigitalPin.P1, 1)
basic.pause(TESTSPEED)
p0 = pins.digitalReadPin(DigitalPin.P0)
p2 = pins.digitalReadPin(DigitalPin.P2)
if (p0 == 1) {
led.plot(2, 1)
ok1 = ok1 + 1
}
if (p2 == 1) {
led.plot(3, 1)
ok1 = ok1 + 1
}
// set back to an input
p0 = pins.digitalReadPin(DigitalPin.P1)
// ## P2 out low, read from P0 and P1
ok2 = 0
pins.digitalWritePin(DigitalPin.P2, 0)
basic.pause(TESTSPEED)
p0 = pins.digitalReadPin(DigitalPin.P0)
p1 = pins.digitalReadPin(DigitalPin.P1)
if (p0 == 0) {
led.plot(0, 2)
ok2 = ok2 + 1
}
if (p1 == 0) {
led.plot(1, 2)
ok2 = ok2 + 1
}
// ## P2 out high, read from P0 and P1
pins.digitalWritePin(DigitalPin.P2, 1)
basic.pause(TESTSPEED)
p0 = pins.digitalReadPin(DigitalPin.P0)
p1 = pins.digitalReadPin(DigitalPin.P1)
if (p0 == 1) {
led.plot(2, 2)
ok2 = ok2 + 1
}
if (p1 == 1) {
led.plot(3, 2)
ok2 = ok2 + 1
}
p2 = pins.digitalReadPin(DigitalPin.P2)
// ## Assess final test status
if (ok0 == 4) {
led.plot(4, 0)
}
basic.pause(TESTSPEED)
if (ok1 == 4) {
led.plot(4, 1)
}
basic.pause(TESTSPEED)
if (ok2 == 4) {
led.plot(4, 2)
}
basic.pause(TESTSPEED)
if (ok0 + ok1 + ok2 == 12) {
// all tests passed
led.plot(4, 4)
}
// ## Test cycle finished
basic.pause(1000)
}
// intentionally don't clear A and B flags, so main loop can process them.
}
/**
* - show number of dots on screen (0..25) to represent temperature in celcius
*/
function testTemperature() {
while (!AWasPressed && !BWasPressed) {
let temp = input.temperature() - 10
temp = Math.clamp(0, 25, temp)
dots025.plotFrame(temp)
basic.pause(500)
}
}
function testButtonB() {
basic.plotLeds(`
. # # . .
. # . # .
. # # . .
. # . # .
. # # . .
`)
// wait for B pressed
while (!input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
basic.plotLeds(`
. . # # .
. . # . #
. . # # .
. . # . #
. . # # .
`)
// wait for B released
while (input.buttonIsPressed(Button.B)) {
basic.pause(100)
}
basic.plotLeds(`
. # # . .
. # . # .
. # # . .
. # . # .
. # # . .
`)
basic.pause(1000)
}
function testTiltY() {
basic.clearScreen()
let prevy = 0
while (!AWasPressed && !BWasPressed) {
basic.pause(100)
let y = input.acceleration(Dimension.Y)
let y2 = y / 512 + 2
let y3 = Math.clamp(0, 4, y2)
// sticky trace
led.plot(0, y3)
// middle line is actual/live
if (y3 != prevy) {
led.unplot(2, prevy)
prevy = y3
}
led.plot(2, y3)
// bottom line is -4G, -2G, 1G, +2G, +4G
if (y <= -2048) {
led.plot(4, 0)
} else if (y <= -1024) {
led.plot(4, 1)
} else if (y <= 1024) {
led.plot(4, 2)
} else if (y <= 2048) {
led.plot(4, 3)
} else {
led.plot(4, 4)
}
}
}
function testTiltZ() {
basic.clearScreen()
while (!AWasPressed && !BWasPressed) {
let z = input.acceleration(Dimension.Z)
if (z < -2000) {
basic.plotLeds(`
# . . . #
# . . . #
# . . . #
# . . . #
# . . . #
`)
} else if (z <= -1030) {
basic.plotLeds(`
. # . # .
. # . # .
. # . # .
. # . # .
. # . # .
`)
} else if (z <= 1000) {
basic.plotLeds(`
. . # . .
. . # . .
. . # . .
. . # . .
. . # . .
`)
} else if (z <= 1030) {
basic.plotLeds(`
. . . . .
. . . . .
# # # # #
. . . . .
. . . . .
`)
} else if (z <= 2000) {
basic.plotLeds(`
. . . . .
# # # # #
. . . . .
# # # # #
. . . . .
`)
} else {
basic.plotLeds(`
# # # # #
. . . . .
. . . . .
. . . . .
# # # # #
`)
}
basic.pause(100)
}
}
function startIOMonitor() {
input.onButtonPressed(Button.A, () => {
AWasPressed = true
})
input.onButtonPressed(Button.B, () => {
BWasPressed = true
})
input.onShake(() => {
wasShake = true
})
}