Skip to content

Instantly share code, notes, and snippets.

@mathdoodle
Last active July 7, 2020 19:27
Show Gist options
  • Save mathdoodle/e176de7beb85f36df721954ea81ab65f to your computer and use it in GitHub Desktop.
Save mathdoodle/e176de7beb85f36df721954ea81ab65f to your computer and use it in GitHub Desktop.
Bacon.js Snake Tutorial

Bacon.js Snake Tutorial

Overview

Tutorial

export default function() {
describe("...", function() {
it("should ...", function() {
expect(true).toBeTruthy()
})
})
}
<!DOCTYPE html>
<html>
<head>
<!-- STYLES-MARKER -->
<style>
/* STYLE-MARKER */
</style>
<script src='https://jspm.io/system.js'></script>
<!-- SHADERS-MARKER -->
<!-- SCRIPTS-MARKER -->
</head>
<body>
<button id='clickMe'>Click me!</button>
<div id='output'></div>
<button class='left'>Left</button>
<button class='right'>Right</button>
<button class='tick'>Tick</button>
<div>
"Direction: "
<span id='direction'></span>
</div>
<div>
"Position: "
<span id='position'></span>
</div>
<div id='game' class='game'>
</div>
<script>
// CODE-MARKER
</script>
<script>
System.import('./index.js')
</script>
</body>
</html>
import Vector2 from './Vector2';
const gameX = 7;
const gameY = 7;
DomReady.ready(function() {
const clicks = $('#clickMe').asEventStream('click')
// scan is a combinator
const counter = clicks.map(1).scan(0, (x, y)=> x + y)
counter.onValue(x => $('#output').html(`${x}`))
///////////////////////
const lefts = $("button.left").asEventStream('click');
const rights = $("button.right").asEventStream('click');
const tick = $("button.tick").asEventStream('click');
const actions = lefts.map(() => rotateLeft).merge(rights.map(() => rotateRight));
// We start by heading upwards
const startDirection = new Vector2(0, 1);
const direction = actions.scan(startDirection, (x, f) => f(x));
const currentDirection = direction.sampledBy(tick);
const startPosition = new Vector2(0, 0)
const position = currentDirection.scan(startPosition, (x, y) => x.add(y))
direction.onValue(function(x) { $("#direction").html(`${x}`)})
position.onValue(function(x) { $("#position").html(`${x}`)})
drawGame('#game')
position.map(Array).onValue(drawSnake("#game"))
})
function rotateLeft(pos: Vector2) {
return new Vector2(pos.y, -pos.x)
}
function rotateRight(pos: Vector2) {
return new Vector2(-pos.y, pos.x)
}
function and(a: boolean, b: boolean): boolean {
return a && b;
}
function nonEmpty(x: {length: number}): boolean {
return x.length > 0
}
/**
* This helper function also exusts in Bacon.UI, but we don't have that loaded.
*/
function textFieldValue(textField: JQuery): Bacon.Property<ErrorEvent, string> {
function value(): string {
return textField.val()
}
return textField.asEventStream('keyup').map(value).toProperty(value())
}
function drawGame(sel) {
var game = $(sel)
for (var i = 0; i < gameX; i++) {
var row = $('<div/>').addClass('row')
game.append(row)
for (var j = 0; j < gameY; j++)
row.append($('<div/>').addClass('cell'))
}
}
function drawSnake(sel) {
var game = $(sel)
return function(ps) {
game.find('.cell').removeClass('snake')
for (var i in ps) {
game.find('.row:eq('+ps[i].y+')').find('.cell:eq('+ps[i].x+')').addClass('snake')
}
}
}
function drawApple(sel) {
var game = $(sel)
return function(p) {
game.find('.cell').removeClass('apple')
game.find('.row:eq('+p.y+')').find('.cell:eq('+p.x+')').addClass('apple')
}
}
/*
function Vector2(x, y) {
this.x = x; this.y = y;
this.describe = x + ', ' + y
this.equals = function(p) { return this.x === p.x && this.y === p.y }
this.add = function(p) { return new Vector2((this.x + p.x + gameX) % gameX, (this.y + p.y + gameX) % gameX) }
}
*/
const randomPos = function() {
return new Vector2(Math.floor(Math.random() * 5), Math.floor(Math.random() * 5))
}
/*
function rotateRight(pos) {
return new Vector2(-pos.y, pos.x)
}
function rotateLeft(pos) {
return new Vector2(pos.y, -pos.x)
}
*/
{
"description": "Bacon.js Snake Tutorial",
"dependencies": {
"DomReady": "1.0.0",
"jquery": "2.1.4",
"jasmine": "3.4.0",
"baconjs": "0.7.89"
},
"name": "baconjs-snake-tutorial",
"version": "0.1.0",
"keywords": [
"Bacon",
"baconjs",
"bacon.js",
"Snake",
"Tutorial"
],
"author": "David Geo Holmes"
}
body {
background-color: white;
font-family: 'Comic Sans MS', sans-serif;
}
.row { padding: 0; margin: 0; height: 12px}
.cell {
width: 10px;
height: 10px;
padding: 0;
margin: 1px;
display: inline-block;
background-color: white;
}
.snake { background-color: green }
.apple { background-color: red }
.game {
padding: 5px;
box-shadow: 0 0 5px lightgray;
display: inline-block;
clear: both;
}
<!DOCTYPE html>
<html>
<head>
<!-- STYLES-MARKER -->
<style>
/* STYLE-MARKER */
</style>
<script src='https://jspm.io/system.js'></script>
<!-- SCRIPTS-MARKER -->
</head>
<body>
<script>
// CODE-MARKER
</script>
<script>
System.import('./tests.js')
</script>
</body>
</html>
import Example from './Example.spec'
window['jasmine'] = jasmineRequire.core(jasmineRequire)
jasmineRequire.html(window['jasmine'])
const env = jasmine.getEnv()
const jasmineInterface = jasmineRequire.interface(window['jasmine'], env)
extend(window, jasmineInterface)
const htmlReporter = new jasmine.HtmlReporter({
env: env,
getContainer: function() { return document.body },
createElement: function() { return document.createElement.apply(document, arguments) },
createTextNode: function() { return document.createTextNode.apply(document, arguments) },
timer: new jasmine.Timer()
})
env.addReporter(htmlReporter)
DomReady.ready(function() {
htmlReporter.initialize()
describe("Example", Example)
env.execute()
})
/*
* Helper function for extending the properties on objects.
*/
export default function extend<T>(destination: T, source: any): T {
for (let property in source) {
destination[property] = source[property]
}
return destination
}
{
"allowJs": true,
"checkJs": true,
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"jsx": "react",
"module": "system",
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5",
"traceResolution": true
}
export default class Vector2 {
constructor(public x: number, public y: number) {
}
add(rhs: Vector2): Vector2 {
return new Vector2(this.x + rhs.x, this.y + rhs.y);
}
toString(): string {
return `[${this.x}, ${this.y}]`
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment