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() { }