Published
Edited
Nov 30, 2019
Insert cell
Insert cell
Insert cell
viewof newGame = button("new game");
Insert cell
mutable highScore = 0
Insert cell
{
let h = mutable highScore; // start to use the mutable in this block

h = h || 0;
if ( h < scores) {
mutable highScore = scores; // update the mutable only when condition is met
}
}
Insert cell
// highScore = { // without using mutable will update the highScore whenever scores update itself
// // debugger;
// console.log(this); // this is the previous highScore value
// return Math.max(this || 0, scores);// if this is undefined, use 0 instead
// }
Insert cell

mutable ball = {
newGame;
return {r: scale, x: w/2, y: h - 2*scale, // ball initializes at middle of the canvas
xSpeed: 0, ySpeed: -speed}; // ball initializes to shoot up straight

}
Insert cell
Insert cell
remainingScores = {
const totalScore = _.reduce(breakpoints, (accumulator, value) => accumulator + value*bricks[0].length, 0);
return totalScore - scores;
}
Insert cell
_ = require("lodash")
Insert cell
mutable scores = {
newGame;
return 0;
}
Insert cell
breakpoints = [7,7,4,4,1,1];
Insert cell
bricks = {
newGame;
const rows = 6;
const columns = Math.floor(w / 50);
const padding = 3, top = 70;
const width = (w - (columns + 1) * padding - 2 * scale) / columns
let bricks = new Array(rows).fill(1).map(() => new Array(columns).fill(1));
eachBrick(bricks, (brick, row, col) => {
bricks[row][col] = {
w: width,
h: scale,
x: scale + padding * (col + 1) + col * width,
y: top + (scale + padding) * row,
color: colors[row],
points: breakpoints[row]
}
});

return bricks;
}
Insert cell
gameloop = {
let n = 0;
let {r, x, xSpeed, y, ySpeed} = mutable ball;
function pauseBall(duration){
mutable ball = {r, x, y, xSpeed, ySpeed}
return Promises.delay(duration, mutable ball)
}
while (!mutable gameover) {
x = x + xSpeed;
y = y + ySpeed;

if (x < r + scale) {
xSpeed = Math.abs(xSpeed);
// yield pauseBall(1000);

}
if (x + r > w - scale) {
xSpeed = -Math.abs(xSpeed);
// yield pauseBall(1000);
}
if (y < r + scale) {
ySpeed = Math.abs(ySpeed);
// yield pauseBall(1000);
}
if (y + r > h) {
ySpeed = -Math.abs(ySpeed);
// yield pauseBall(0);
}

eachBrick(bricks, (brick, row, col) => {
let hit = collide(mutable ball, brick);
if (!hit) return;
bricks[row][col] = null;
(hit == "v") ? ySpeed = -ySpeed : xSpeed = -xSpeed;
mutable scores += brick.points; // when ball hit a break, add the brick's points to score
return true;
});
let px = mutable paddleX;
if (y + r > h - scale
&& x + r > px
&& x - r < px + paddleLength) {
yield pauseBall(0);

ySpeed = -ySpeed;
xSpeed = ((x - px) / paddleLength - 0.5) * Math.PI * speed;
}
mutable ball = {r, x, y, xSpeed, ySpeed};
if ( y + r > h ) {
mutable gameover = true;
yield pauseBall(3000);
}
yield true;
}
}
Insert cell
Insert cell
colors = ["#c84848", "#c66c3a", "#b47a2f", "#a2a229", "#49a049", "#4348c8"]; // array of colors
Insert cell
function eachBrick(bricks, cb) {
for (let row = 0; row < bricks.length; row++) {
for (let col = 0; col < bricks[row].length; col++) {
let brick = bricks[row][col];
if (!brick) continue;
if (cb(brick, row, col)) return;
}
}
}
Insert cell
mutable gameover = {
newGame;
return false;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mutable paddleX = w/2 - paddleLength/2;
Insert cell
mousemove = c.canvas.onmousemove = e => {
if ( paddleLength/2 + scale <= e.offsetX && e.offsetX <= w - paddleLength/2 - scale ) {
mutable paddleX = e.offsetX - paddleLength/2
} else if ( paddleLength/2 + scale > e.offsetX ) {
mutable paddleX = scale
} else {
mutable paddleX = w - paddleLength - scale
}

}
Insert cell
function collide(ball, brick){ // ball will hit brick: change direction and speed, brick will disappear
const {x, y, r, xSpeed, ySpeed} = ball;
let hit = x + r > brick.x && // ball's right side must be on the right side of the left border of the brick
x - r < brick.x + brick.w && // ball's left side must be on the left side of the right border of the brick
y + r > brick.y && // ball's bottom side must be below the top border of the brick
y - r < brick.y + brick.h; // ball's top side mus be above the bottom border of the brick
if (!hit) return false; // if ball not collide with the brick, return false

// if ball and brick collide, then we will consider how the ball moves: vertically or horizontally?
if (y - ySpeed + r <= brick.y || // if the ball backward 1 step will detach the ball's bottom side from the brick's top (assuming the ball is falling, ySpeed is positive), then return v
y - ySpeed - r >= brick.y + brick.h) { // or if the ball backward 1 step will detach ball's top side from the bottom border of the brick (assuming the ball is shooting up, ySpeed is negative), (if so) return v
return "v"; // v means the ball is falling down or shooting up vertically
}
if (x - xSpeed + r <= brick.x || // if the ball backward 1 step will detach ball's right side from the brick's left border (assuming the ball is moving right, xSpeed is positive), then return h
x - xSpeed - r >= brick.x + brick.w) { // or if the ball backward 1 step will detach ball's left side from the right border of the brick, then return h
return "h"; // h means the ball is moving (more) horizontally (than vertically ?)
}
}
Insert cell
draw = {
gameloop;
c.fillStyle="black";
c.fillRect(0,0,w,h);
// draw the thick walls
c.fillStyle = "#444";
c.fillRect(0, 0, w, scale);
c.fillRect(0, 0, scale, h);
c.fillRect(w-scale, 0, scale, h);
// draw the bricks
eachBrick(bricks, (brick, row, col) => {
c.fillStyle = brick.color;
c.fillRect(brick.x, brick.y, brick.w, brick.h);
});
const {r, x, y} = mutable ball;
c.beginPath();
c.fillStyle = "red";
c.arc(x, y, r, 0, 2 * Math.PI);
c.fill();
// c.fillStyle = "green"
// c.font = "15px Arial"
// if ( y + r > h ) c.fillText(`y + r > h : ${y + r > h} gameover: ${gameover}`, (x < w/2)? x + 20 : x - 170, y - 20);

c.fillStyle = "blue";
c.fillRect(mutable paddleX,
h - scale,
paddleLength,
scale);
c.fillStyle = "cyan";
c.fillRect(mutable paddleX, h - scale, 2, 2)


}
Insert cell
videoyt("Aznz6oLbuFQ")
Insert cell
import {videoyt, slider, button} from "@embracelife/tutorial-utilities"
Insert cell
FileAttachment("chart.svg").image()
Insert cell
FileAttachment("chart.png").image()
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more