Category Archives: Miscellaneous

Grand Designs

Grand Designs was an activity that I helped create for young people in 2007. We asked young people to come up with proposals for the design and operation of a supported accommodation unit.

I was inspired by the young people’s creativity and focus on the day. The results are written up in this short briefing.

More on young people and homelessness can be found at www.scsh.org.uk.

custom.ts – Lunar Lander project

[code lang=”js”]
/**
* Custom functions and blocks for interfacing with TFT display
* and supporting the Lunar Lander game.
*
* For documentation on the AdaFruit 1.44" TFT display
* see: https://learn.adafruit.com/adafruit-1-44-color-tft-with-micro-sd-socket
*
* The code to drive the display has been adapted from the code provided (in C++)
* by AdaFruit.
*
* For syntax that makes functions and enums available in the blocks editor
* see https://makecode.microbit.org/blocks/custom
*/

// TFT display commands
// Only the commands actually used are included here. See the ST7735R
// data sheet for the full set of commands.
enum TftCom {
//% block="NOOP" NOOP
NOOP = 0x00,

//% block="SWRESET" Software Reset
SWRESET = 0x01,

//% block="SLPOUT" Sleep Out
SLPOUT = 0x11,

//% block="NORON" Normal Display Mode On (no parameters)
NORON = 0x13,

//% block="INVOFF" Display Inversion Off (0)
INVOFF = 0x20,

//% block="DISPON" Display On (no parameters)
DISPON = 0x29,

//% block="CASET" Column Address Set
CASET = 0x2A,

//% block="RASET" Row Address Set
RASET = 0x2B,

//% block="RAMWR" Memory Write
RAMWR = 0x2C,

//% block="MADCTL" Memory Data Access Control (1)
MADCTL = 0x36,

//% block="COLMOD" Interface Pixel Format (1)
COLMOD = 0x3A,
//% block="GMCTRP1" Gamma (+polarity) Corr’n Characteristics Setting (16)

//% block="FRMCTR1" Frame Rate Control (in normal mode / full colours) (3)
FRMCTR1 = 0xB1,
//% block="FRMCTR2" Frame Rate Control (in idle mode / 8 colours) (3)
FRMCTR2 = 0xB2,
//% block="FRMCTR3" Frame Rate Control (In Partial mode/ full colours) (3)
FRMCTR3 = 0xB3,

//% block="INVCTR" Display Inversion Control (1)
INVCTR = 0xB4,
//% block="PWCTR1" Power Control 1 (3)

PWCTR1 = 0xC0,
//% block="PWCTR2" Power Control 2 (1)
PWCTR2 = 0xC1,
//% block="PWCTR3" Power Control 3 (2)
PWCTR3 = 0xC2,
//% block="PWCTR4" Power Control 4 (2)
PWCTR4 = 0xC3,
//% block="PWCTR5" Power Control 5 (2)
PWCTR5 = 0xC4,
//% block="VMCTR1" VCOM Control 1 (VCOM voltage setting) (1)
VMCTR1 = 0xC5,

GMCTRP1 = 0xE0,
//% block="GMCTRN1" Gamma (-polarity) Corr’n Characteristics Setting (16)
GMCTRN1 = 0xE1,

//% block="DELAY"
DELAY = 0xFFFF
}

// Canned sound options (for game sound effects and debugging / startup
enum LunarSound {
//% block="Start1" First sound on startup sequence
Start1,
//% block="Start2" Second sound on startup sequence
Start2,
//% block="MainBooster" Firing main enginge
MainBooster,
//% block="Crash" Crash
Crash,
//% block="Landing" Successful Landing
Landing,
//% block="Original" First Test Phrase
Default
}
// Custom sprite options
enum TftSprite {
//% block="Y"
Y,
//% block="o"
o,
//% block="u"
u,
//% block="C"
C,
//% block="r"
r,
//% block="s"
s,
//% block="h"
h,
//% block="e"
e,
//% block="d"
d,
//% block="L"
L,
//% block="n"
n,
//% block="!"
pling,
//% block="J"
J,
//% block="b"
b,
//% block="’"
apostrophe,
//% block=G
G,
//% block=a
a,

//% block=boosterFlame
boosterFlame
}

enum LandscapeSegment {
//% block=Crater1
Crater1,
//% block=Crater2
Crater2,
//% block=RoughTerrain1
RoughTerrain1,
//% block=RoughTerrain2
RoughTerrain2,
//% block=LandingSite
LandingSite,
//% block=Mountain1
Mountain1,
//% block=Mountain2
Mountain2
}

/**
* Custom blocks
*/

//% weight = 90 color=#0f0f80
namespace tools {

/**
* Add two Array<number> variables together and return an array. I can’t see
this built in. Crazy. Have I missed or is it just a glaring omission?
*/

//% block
export function addNumberArray(array1: Array<number>, array2: Array<number>): Array<number> {
let returnArray = array1
let array2Length = array2.length
for (let i = 0; i < array2Length; i++) {
let element = array2[i]
returnArray.push(element)
}
return returnArray
}
}

//% weight=100 color=#0fbc51 icon=""
namespace lunar {

/*
* Draw the landscape, including setting up the
* allHeights array for collision detection
*/

//% block
export function drawLandscape() {
xCursor = 0
segmentHeights = display.drawLandscapeSegment(xCursor, LandscapeSegment.Crater1)
allHeights = segmentHeights
xCursor = xCursor + segmentHeights.length

segmentHeights = display.drawLandscapeSegment(xCursor, LandscapeSegment.RoughTerrain1)
xCursor = xCursor + segmentHeights.length
allHeights = tools.addNumberArray(allHeights, segmentHeights)

segmentHeights = display.drawLandscapeSegment(xCursor, LandscapeSegment.Mountain2)
xCursor = xCursor + segmentHeights.length
allHeights = tools.addNumberArray(allHeights, segmentHeights)

landingStartX = (xCursor + 8) * display.displayScale()

segmentHeights = display.drawLandscapeSegment(xCursor, LandscapeSegment.LandingSite)
xCursor = xCursor + segmentHeights.length
allHeights = tools.addNumberArray(allHeights, segmentHeights)

landingEndX = (xCursor – 8) * display.displayScale()

segmentHeights = display.drawLandscapeSegment(xCursor, LandscapeSegment.RoughTerrain2)
xCursor = xCursor + segmentHeights.length
allHeights = tools.addNumberArray(allHeights, segmentHeights)

segmentHeights = display.drawLandscapeSegment(xCursor, LandscapeSegment.Crater1)
xCursor = xCursor + segmentHeights.length
allHeights = tools.addNumberArray(allHeights, segmentHeights)

segmentHeights = display.drawLandscapeSegment(xCursor, LandscapeSegment.Mountain1)
xCursor = xCursor + segmentHeights.length
allHeights = tools.addNumberArray(allHeights, segmentHeights)
}

/**
* whiteNoise(duration: number, pitch: number)
* Make white noise for about duration milliseconds.
* The pitch value has (some) influence on the pitch.
*/
//% block
export function whiteNoise(duration: number, pitch: number) {

let total = 0
duration = duration * 1000

while (total < duration) {
pins.digitalWritePin(DigitalPin.P0, 0)
let wait = pitch + Math.random(500)
total = total + wait
control.waitMicros(wait)
pins.digitalWritePin(DigitalPin.P0, 1)
}
}

/**
* explosion()
* Explosion sound lasting 1000 milliseconds
*/
//% block
export function explosion() {

let total = 0
let duration = 1000 * 1000
let baseWait = 1000

while (total < duration) {
pins.digitalWritePin(DigitalPin.P0, 0)
let wait = baseWait + Math.random(3000)
total = total + wait
control.waitMicros(wait)
pins.digitalWritePin(DigitalPin.P0, 1)
if (baseWait > 200) {
baseWait = baseWait – 50
}
}
}

/**
* Play a brief sound so we know the button has been pressed.
*/
//% block
export function clickSound(sound: LunarSound) {

if (sound == LunarSound.Default) {
music.setTempo(180)
music.playTone(262, music.beat(BeatFraction.Whole))
music.rest(music.beat(BeatFraction.Quarter))
return
}

if (sound == LunarSound.MainBooster) {
for (let i = 0; i < 500; i++) {
pins.digitalWritePin(DigitalPin.P0, 0)
control.waitMicros(200 + Math.random(501))
pins.digitalWritePin(DigitalPin.P0, 1)
}
}

if (sound == LunarSound.Start1) {
music.setTempo(180)
music.playTone(262, music.beat(BeatFraction.Whole))
music.rest(music.beat(BeatFraction.Quarter))
return
}

if (sound == LunarSound.Start2) {
music.setTempo(180)
music.playTone(392, music.beat(BeatFraction.Whole))
music.rest(music.beat(BeatFraction.Quarter))
}
}

}

/**
* Code for interfacing with the TFT
* API
* setupDisplay()
* drawPixel(x: number, y: number, colour: number)
*
*
*/
namespace display {

//% block
export function displayScale(): number {
return 32
}

/*
* The display width in ‘working coordinates’. These are pixel values * displayScale()
*/
//% block
export function displayWidth(): number {
return 128 * displayScale()
}

/**
* The display height in ‘working coordinates’. These are pixel values * displayScale()
*/
//% block
export function displayHeight(): number {
return 128 * displayScale()
}

/**
* Convert a working coordinate to an actual pixel coordinate.
* Don’t expose this in final code. It should be internal.
*/
//% block
export function roundedPixel(value: number): number {
let adjusted = value / displayScale();
adjusted = adjusted + (value & (displayScale() – 1) ? 1 : 0)
return adjusted;
}

/**
* Draw a single pixel of a given colour
*/
//% block
export function drawPixel(x: number, y: number, colour: number) {
if (outOfBounds(x, y)) {
return
}

setAddrWindow(x, y, x + 1, y + 1);
// send data (16 bits in two bytes)
tftCom(TftCom.RAMWR, [colour >> 8, colour])
}

function outOfBounds(v1: number, v2 = 0, v3 = 0, v4 = 0): boolean {
if (v1 < 0 || v1 > 127) {
return true
}
if (v2 < 0 || v2 > 127) {
return true
}
if (v3 < 0 || v3 > 127) {
return true
}
if (v4 < 0 || v4 > 127) {
return true
}
return false
}

/**
* Draw a line of a given colour
*/
//% block
export function drawLine(x0: number, y0: number, x1: number, y1: number, colour: number) {
let xDelta = x1 – x0
let yDelta = y1 – y0

if (Math.abs(yDelta) > Math.abs(xDelta)) {
let ySteps = Math.abs(yDelta / displayScale())
let xIncrement = xDelta == 0 ? 0 : xDelta / ySteps
let yIncrement = yDelta > 0 ? displayScale() : -1 * displayScale()

let x = x0
let y = y0;
for (let steps = 0; steps <= ySteps; steps++) {
drawPixel(roundedPixel(x), roundedPixel(y), colour)
x = x + xIncrement
y = y + yIncrement
}
return
}

let xSteps = Math.abs(xDelta / displayScale())
let yIncrement = yDelta == 0 ? 0 : yDelta / xSteps;
let xIncrement = xDelta > 0 ? displayScale() : -1 * displayScale()

let y = y0;
let x = x0
for (let steps = 0; steps <= xSteps; steps++) {
drawPixel(roundedPixel(x), roundedPixel(y), colour)
y = y + yIncrement
x = x + xIncrement
}
}

/**
* Set the address window
*/
//% block
export function setAddrWindow(x0: number, y0: number, x1: number, y1: number) {

if (outOfBounds(x0, y0, x1, y1)) {
return
}
// set the column
tftCom(TftCom.CASET, [0x00, x0 + 2, 0x00, x1 + 2]) // 2 is an adjust for thr AdaFruit 1.44 display
// set the row
tftCom(TftCom.RASET, [0x00, y0 + 3, 0x00, y1 + 3]) // 3 is an adjust for thr AdaFruit 1.44 display
}

/**
* Fill a rectangle with a given colour
*/
//%block
export function fillRect(x: number, y: number, width: number, height: number, colour: number) {

if (outOfBounds(x, y)) {
return;
}

if ((x + width) > 128) {
width = 128 – x;
}

if ((y + height) > 128) {
height = 128 – y;
}

let hiColour = (colour >> 8) % 256;
let loColour = colour % 256;

setAddrWindow(x, y, x + width – 1, y + height – 1);

// we are going to manually implement the RAMWR command here because
// we have custom parameters. See comments in tftCom for details
// of what’s going on here.
pins.digitalWritePin(DigitalPin.P1, 0); // command/data = command
pins.digitalWritePin(DigitalPin.P16, 0); // select the TFT as SPI target
pins.spiWrite(TftCom.RAMWR);
pins.digitalWritePin(DigitalPin.P1, 1); // command/data = data

for (let indexY = height; indexY > 0; indexY–) {
for (let indexX = width; indexX > 0; indexX–) {
pins.spiWrite(hiColour)
pins.spiWrite(loColour)
}
}

pins.digitalWritePin(DigitalPin.P16, 1) // de-elect the TFT as SPI target
pins.digitalWritePin(DigitalPin.P1, 0) // command/data = command
}

/**
* Write a command to the TFT display
*/
//% block
export function tftCom(command: TftCom, params: Array<number>) {

// handle the pseudo ‘DELAY’ command – provides a delay in milliseconds
if (command == TftCom.DELAY) {
let waitTime: number = 500
if (params.length == 1) {
waitTime = params[0]
}
basic.pause(waitTime)
return
}

// let the TFT know we’re sending a command (rather than data)
pins.digitalWritePin(DigitalPin.P1, 0) // command/data = command
// select the TFT controller
pins.digitalWritePin(DigitalPin.P16, 0) // select the TFT as SPI target

pins.spiWrite(command)

// let the TFT know we’re sending data bytes (rather than a command)
pins.digitalWritePin(DigitalPin.P1, 1) // command/data = data

for (let dataItem of params) {
pins.spiWrite(dataItem)
}

// de-select the TFT controller
pins.digitalWritePin(DigitalPin.P16, 1) // de-elect the TFT as SPI target

// restore pin to zero (for tidiness – not required)
pins.digitalWritePin(DigitalPin.P1, 0) // command/data = command
}

/**
* Setup and clear screen ready for used
*/
//% block
export function setupScreen() {

pins.spiFrequency(4000000) // try a fast rate for serial bus

tftSetup()

fillRect(
0,
0,
128,
128,
0
)
}

/**
* Do initial set up for display. (Required before any drawing begins.)
*/
//% block
function tftSetup() {

// General Setup (for various display types)

// 1. Software Reset
tftCom(TftCom.SWRESET, [])
tftCom(TftCom.DELAY, [150]) // we need ot wait at least 120ms

// 2. Exit Sleep Mode
tftCom(TftCom.SLPOUT, [])
tftCom(TftCom.DELAY, [150]) // we need to wait at least 120ms

// 3. Frame rate ctrl – normal mode
tftCom(TftCom.FRMCTR1, [0x01, 0x2C, 0x2D])

// 4. Frame rate ctrl – idle mode
tftCom(TftCom.FRMCTR2, [0x01, 0x2C, 0x2D])

// 5. Frame rate ctrl – dot inversion mode
tftCom(TftCom.FRMCTR3, [0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D])

// 6. Display inversion ctrl – no inversion
tftCom(TftCom.INVCTR, [0x07])

// 7. Power Control -4.6v, Auto Mode
tftCom(TftCom.PWCTR1, [0xA2, 0x02, 0x84])

// 8. Power control,VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD
tftCom(TftCom.PWCTR2, [0xC5])

// 9: Power control, Opamp current small + Boost Frequency
tftCom(TftCom.PWCTR3, [0x0A, 0x00])

// 10: Power control, BCLK/2, Opamp current small & Medium low
tftCom(TftCom.PWCTR4, [0x8A, 0x2A])

// 11: Power control
tftCom(TftCom.PWCTR5, [0x8A, 0xEE])

// 12: Power control
tftCom(TftCom.VMCTR1, [0x0E])

// 13. Don’t invert display
tftCom(TftCom.INVOFF, [])

// 14: Memory access control (directions)
tftCom(TftCom.MADCTL, [0xC8])

// 15: set color mode, 16-bit colour
tftCom(TftCom.COLMOD, [0x05])

// 1.44 display specific set up
tftCom(TftCom.CASET, [0x00, 0x00, 0x00, 0x7F])
tftCom(TftCom.RASET, [0x00, 0x00, 0x00, 0x7F])

// Further General Setup (for various display types)

//1: Gamma Correction (magic numbers direct from AdaFruit code)
tftCom(TftCom.GMCTRP1, [0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10])

//2: Gamma Correction (magic numbers direct from AdaFruit code)
tftCom(TftCom.GMCTRN1, [0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10])

// 3: Normal Display On
tftCom(TftCom.NORON, [])
tftCom(TftCom.DELAY, [10])

// 4: Main screen turn on
tftCom(TftCom.DISPON, [])
tftCom(TftCom.DELAY, [100])
}

/**
* Set display a spriteAt
*/
//% block
export function fastSpriteAt(x: number, y: number, state: boolean) {

x = roundedPixel(x)
y = roundedPixel(y)

if (outOfBounds(x, y)) {
return
}

let hiSpriteColour = (0xF800 >> 8) & 255;
let loSpriteColour = 0xF800 & 255;

let spritePattern = [
false, false, false, true, true, false, false, false,
false, false, true, false, false, true, false, false,
false, true, false, false, false, false, true, false,
false, false, true, false, false, true, false, false,
false, false, true, true, true, true, false, false,
false, true, false, false, false, false, true, false,
true, false, false, false, false, false, false, true,
true, false, false, false, false, false, false, true
];

setAddrWindow(x, y, x + 7, y + 7)
// we are going to manually implement the RAMWR command here because
// we have custom parameters. See comments in tftCom for details
// of what’s going on here.
pins.digitalWritePin(DigitalPin.P1, 0); // command/data = command
pins.digitalWritePin(DigitalPin.P16, 0); // select the TFT as SPI target
pins.spiWrite(TftCom.RAMWR);
pins.digitalWritePin(DigitalPin.P1, 1); // command/data = data

for (let pixelOn of spritePattern) {

if (pixelOn && state) {
pins.spiWrite(hiSpriteColour)
pins.spiWrite(loSpriteColour)
}
else {
pins.spiWrite(0)
pins.spiWrite(0)
}
}

pins.digitalWritePin(DigitalPin.P16, 1) // de-elect the TFT as SPI target
pins.digitalWritePin(DigitalPin.P1, 0); // command/data = command
}

//% block
export function writeYouCrashed(xPosition: number, yPosition: number, state: boolean) {

let xCursor = xPosition
let yCursor = yPosition

customSpriteAt(TftSprite.Y, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.o, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.u, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.C, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.r, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.a, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.s, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.h, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.e, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.d, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.pling, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

}

//% block
export function writeYouLanded(xPosition: number, yPosition: number, state: boolean) {

let xCursor = xPosition
let yCursor = yPosition

customSpriteAt(TftSprite.Y, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.o, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.u, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.L, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.a, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.n, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.d, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.e, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.d, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.pling, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()
}

//% block
export function writeJacobsLander(xPosition: number, yPosition: number, state: boolean) {

let xCursor = xPosition
let yCursor = yPosition

customSpriteAt(TftSprite.J, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.a, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.C, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.o, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.b, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.apostrophe, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.s, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.L, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.a, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.n, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.d, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.e, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

customSpriteAt(TftSprite.r, xCursor, yCursor, state)
xCursor = xCursor + 7 * displayScale()

}

function getPatternForCustomSprite(sprite: TftSprite): Array<string> {

switch (sprite) {
case TftSprite.G:
return [
" XXXX",
" X ",
" X ",
" X XX",
" X X",
" XXXX"

];

case TftSprite.Y:
return [
"X X ",
" X X ",
" X ",
" X ",
" X ",
" X "
];

case TftSprite.o:
return [
" ",
" ",
" XX ",
"X X",
"X X",
" XX "
];

case TftSprite.u:
return [
" ",
" ",
"X X ",
"X X ",
"X XX ",
" XX X"
];

case TftSprite.C:
return [
" XXXXX",
"X ",
"X ",
"X ",
"X ",
" XXXXX"
];

case TftSprite.r:
return [
" ",
"X ",
"XXXXXX",
"X ",
"X ",
"X ",
"X "
];

case TftSprite.a:
return [
" ",
" ",
" XXX ",
"X X ",
"X X ",
" XXXXX"
];

case TftSprite.s:
return [
" ",
" XXXXX",
"X ",
" XXXX ",
" X",
"XXXXX "
];

case TftSprite.h:
return [
"X ",
"X ",
"X ",
"XXXXX ",
"X X",
"X X"
];

case TftSprite.e:
return [
" ",
" XXXX ",
"X X",
"XXXXX ",
"X ",
" XXXXX"
];

case TftSprite.d:
return [
" X",
" X",
" X",
" XXXXX",
"X X",
" XXXXX"
];

case TftSprite.L:
return [
"X ",
"X ",
"X ",
"X ",
"X ",
"XXX "
];

case TftSprite.n:
return [
" ",
" ",
"X ",
"XXXX ",
"X X ",
"X X "
];

case TftSprite.pling:
return [
" X ",
" X ",
" X ",
" X ",
" ",
" X "
];

case TftSprite.J:
return [
"XXXXXX",
" X ",
" X ",
" X ",
"X X ",
" XX "
];

case TftSprite.b:
return [
"X ",
"X ",
"X ",
"XXX ",
"X X ",
"XXX "
];

case TftSprite.apostrophe:
return [
" X ",
" X ",
" X ",
" ",
" ",
" "
];

case TftSprite.boosterFlame:
return [
"XXX",
" X ",
];
}

return ["X"]

}

/*
function getPatternForCustomSprite(sprite: TftSprite): Array<string> {

switch (sprite) {
case TftSprite.G:
return [
" XXXX",
" X ",
" X ",
" X XX",
" X X",
" XXXX"
];

case TftSprite.a:
return [
" ",
" XX ",
" X ",
" XXX ",
" X X ",
" XXXX"
];

case TftSprite.boosterFlame:
return [
"XXX",
" X ",
];
}

return ["X"]

}

*/

/**
* Set display a spriteAt
*/
//% block
export function customSpriteAt(sprite: TftSprite, x: number, y: number, state: boolean) {

x = roundedPixel(x)
y = roundedPixel(y)

if (outOfBounds(x, y)) {
return
}

let hiSpriteColour = (0xF800 >> 8) & 255;
let loSpriteColour = 0xF800 & 255;

let spritePattern = getPatternForCustomSprite(sprite)

let spriteWidth = spritePattern[0].length – 1
let spriteHeight = spritePattern.length – 1

setAddrWindow(x, y, x + spriteWidth, y + spriteHeight)
// we are going to manually implement the RAMWR command here because
// we have custom parameters. See comments in tftCom for details
// of what’s going on here.
pins.digitalWritePin(DigitalPin.P1, 0); // command/data = command
pins.digitalWritePin(DigitalPin.P16, 0); // select the TFT as SPI target
pins.spiWrite(TftCom.RAMWR);
pins.digitalWritePin(DigitalPin.P1, 1); // command/data = data

for (let spriteLine of spritePattern) {

for (let character of spriteLine) {

if (character == "X" && state == true) {
pins.spiWrite(hiSpriteColour)
pins.spiWrite(loSpriteColour)
}
else {
pins.spiWrite(0)
pins.spiWrite(0)
}
}
}

pins.digitalWritePin(DigitalPin.P16, 1) // de-elect the TFT as SPI target
pins.digitalWritePin(DigitalPin.P1, 0); // command/data = command
}

/**
* Alternative approach to landscape. A series of custom segments. When each segment is drawn it
* returns an array of heights for collision detection.
*/

//% block
export function drawLandscapeSegment(xCursor: number, segment: LandscapeSegment): Array<number> {

let codedDeltas = codedDeltasForSegment(segment)

let peaks: Array<number> = []

let yCursor = 128 – 6

for (let code of codedDeltas) {

if (code == "=") {
drawPixel(xCursor, yCursor, 65535)
peaks.push(yCursor * displayScale())
xCursor = xCursor + 1
}

if (code == "+") {
yCursor = yCursor – 1
drawPixel(xCursor, yCursor, 65535)
peaks.push(yCursor * displayScale())
xCursor = xCursor + 1
}

if (code == "-") {
yCursor = yCursor + 1
drawPixel(xCursor, yCursor, 65535)
peaks.push(yCursor * displayScale())
xCursor = xCursor + 1
}

if (code == "!") {
yCursor = yCursor + 1
drawPixel(xCursor, yCursor, 65535)
}

if (code == "^") {
yCursor = yCursor – 1
drawPixel(xCursor, yCursor, 65535)
peaks.pop()
peaks.push(yCursor * displayScale())
}
}

return peaks
}
/**
* Returns a string of coded deltas.
* = -> (1,0)
* + -> (1,-1)
* – -> (1,1)
* ! -> (0,1)
* ^ -> (0,-1)
*/
function codedDeltasForSegment(segment: LandscapeSegment): string {

switch (segment) {
case LandscapeSegment.Crater1:
return "=+-!–=-=+=++^+-"

case LandscapeSegment.Mountain1:
return "=^++^++^+==!=!=—-=!"

case LandscapeSegment.Mountain2:
//return "=^^^^^^^^^^^^^^^^========!!!!!!!!!!!!!=" // TEST mountain – really high and flat
return "=+^^^^+=+^^^^^^++===–!!!!!!——!"

case LandscapeSegment.LandingSite:
return "===+++==—==============+++==—="
}

return "===+-==++-=-===++–===+-="
}

}

[/code]