diff --git a/images/embiggenBtn.png b/images/embiggenBtn.png new file mode 100644 index 0000000..8d85be6 Binary files /dev/null and b/images/embiggenBtn.png differ diff --git a/images/embiggenSmBtn.png b/images/embiggenSmBtn.png new file mode 100644 index 0000000..5ddef06 Binary files /dev/null and b/images/embiggenSmBtn.png differ diff --git a/images/playBtn.png b/images/playBtn.png new file mode 100644 index 0000000..7bd8851 Binary files /dev/null and b/images/playBtn.png differ diff --git a/images/playSmBtn.png b/images/playSmBtn.png new file mode 100644 index 0000000..97a50d3 Binary files /dev/null and b/images/playSmBtn.png differ diff --git a/images/resetBtn.png b/images/resetBtn.png new file mode 100644 index 0000000..e140108 Binary files /dev/null and b/images/resetBtn.png differ diff --git a/images/resetSmBtn.png b/images/resetSmBtn.png new file mode 100644 index 0000000..f63c96b Binary files /dev/null and b/images/resetSmBtn.png differ diff --git a/images/shuffleBtn.png b/images/shuffleBtn.png new file mode 100644 index 0000000..9af0052 Binary files /dev/null and b/images/shuffleBtn.png differ diff --git a/images/shuffleSmBtn.png b/images/shuffleSmBtn.png new file mode 100644 index 0000000..7f9cd12 Binary files /dev/null and b/images/shuffleSmBtn.png differ diff --git a/images/stopBtn.png b/images/stopBtn.png new file mode 100644 index 0000000..75f38a8 Binary files /dev/null and b/images/stopBtn.png differ diff --git a/images/stopSmBtn.png b/images/stopSmBtn.png new file mode 100644 index 0000000..f88922e Binary files /dev/null and b/images/stopSmBtn.png differ diff --git a/images/unbiggenBtn.png b/images/unbiggenBtn.png new file mode 100644 index 0000000..7d3000a Binary files /dev/null and b/images/unbiggenBtn.png differ diff --git a/images/unbiggenSmBtn.png b/images/unbiggenSmBtn.png new file mode 100644 index 0000000..5597ae3 Binary files /dev/null and b/images/unbiggenSmBtn.png differ diff --git a/package.json b/package.json index b4b3b30..2600ddb 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "exe": "./node_modules/.bin/electron main.js main.js", "start": "npm run watch", "clean": "cross-conf-env rimraf npm_package_config_bundle", - "build": "cross-conf-env browserify -s npm_package_name npm_package_config_app -o npm_package_config_bundle -p [tsify --target='es6'] -t [preprocessify --contextFile build/debug.json] --no-bundle-external --debug", - "watch": "cross-conf-env watchify -s npm_package_name npm_package_config_app -o npm_package_config_bundle -p [tsify --target='es6'] -t [preprocessify --contextFile build/debug.json] --no-bundle-external --debug -dv" + "build": "cross-conf-env browserify -s npm_package_name npm_package_config_app -o npm_package_config_bundle -p [tsify --target=\"es6\"] -t [preprocessify --contextFile build/debug.json] --no-bundle-external --debug", + "watch": "cross-conf-env watchify -s npm_package_name npm_package_config_app -o npm_package_config_bundle -p [tsify --target=\"es6\"] -t [preprocessify --contextFile build/debug.json] --no-bundle-external --debug -dv" }, "browserify": { "transform": [] diff --git a/sounds/click.ogg b/sounds/click.ogg new file mode 100644 index 0000000..18428dd Binary files /dev/null and b/sounds/click.ogg differ diff --git a/sounds/tada.ogg b/sounds/tada.ogg new file mode 100644 index 0000000..fca0987 Binary files /dev/null and b/sounds/tada.ogg differ diff --git a/sounds/tbone.ogg b/sounds/tbone.ogg new file mode 100644 index 0000000..55a6b9c Binary files /dev/null and b/sounds/tbone.ogg differ diff --git a/sounds/wetfoot.ogg b/sounds/wetfoot.ogg new file mode 100644 index 0000000..b35127a Binary files /dev/null and b/sounds/wetfoot.ogg differ diff --git a/src/index.ts b/src/index.ts index ee941d6..d5c6382 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ document.body.appendChild(renderer.view); // You need to create a root container that will hold the scene you want to draw. const stage:PIXI.Container = new PIXI.Container(); +/* // Declare a global variable for our sprite so that the animate function can access it. let bunny:PIXI.Sprite = null; @@ -28,14 +29,556 @@ PIXI.loader.add('bunny', 'images/bunny.jpeg').load(function (loader:PIXI.loaders // kick off the animation loop (defined below) animate(); }); - +*/ function animate() { // start the timer for the next animation loop requestAnimationFrame(animate); - - // each frame we spin the bunny around a bit - bunny.rotation += 0.01; - // this is the main render call that makes pixi draw your container and its children. renderer.render(stage); } + + +////////////////////////////////////////////////// + +enum Direction { + Up = 1, + Down, + Left, + Right +} + +////////////// Checkerboard ////////////////////// + +class Checkerboard { + + roundOver:boolean = false; + + private grid:CheckerboardCell[][]; + private cursorPosition:PIXI.Point = new PIXI.Point(-1, -1); + + constructor(numRows:number, numColumns:number) { + if (numRows >= minBoardSize) { + if (numColumns >= minBoardSize) { + if (numColumns <= maxBoardSize) { + if (numRows <= maxBoardSize) { + this.createBoard(numRows, numColumns); + } + } + } + } else { + throw new Error("Lousy board size") + } + } + + get numRows():number { + return this.grid.length; + } + + get numColumns():number { + return this.grid[0].length; + } + + get isEmpty():boolean { + return this.cursorPosition.y < 0; + } + + reset():void { + this.roundOver = false; + this.cursorPosition.x = this.cursorPosition.y = -1; + for (let row = 0; row 0) { + nextPosition = new PIXI.Point(col, row-1) + } + break; + case Direction.Right: + if (col < this.numColumns-1) { + nextPosition = new PIXI.Point(col+1, row); + } + break; + case Direction.Down: + if (row < this.numRows-1) { + nextPosition = new PIXI.Point(col, row+1); + } + break; + case Direction.Left: + if (col > 0) { + nextPosition = new PIXI.Point(col-1, row); + } + break; + } + return nextPosition; + } + +} + +////////////////////////////////////////////////// + +interface LinkedList { + next:LinkedList; +} + +/////////////// Cell ///////////////////////////// + +class CheckerboardCell implements LinkedList { + next:LinkedList = null; + pointerDirection: Direction; + occupied:boolean = false; + visited:boolean = false; + + reset():void { + this.occupied = this.visited = false; + } + + toString():string { + return "[Cell o:" + this.occupied + " v:" + this.visited + "]"; + } +} + +////////////////// utils ///////////////////////// + +function randomDirection():Direction { + return randomInt(1,4) as Direction; +} + +function randomInt(min:number, max:number):number { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +//////////////// Floyd's algorithm /////////////// + +function willLoop(firstCell:LinkedList):boolean { + let result:boolean = false; // Optimism + // empty lists do not loop + if (null == firstCell) { + return result; + } + + let tortoise:LinkedList; + let hare:LinkedList; + + tortoise = hare = firstCell; + + while (true) { + tortoise = tortoise.next; + // hare will move twice as fast as tortoise + if (null == hare.next) { + // we escaped, no loop found + break; + } else { + hare = hare.next.next; + } + + // did either hit the edge? + if ((null == tortoise) || (null == hare)) { + // we escaped + break; + } + + if (tortoise == hare) { + // we looped! + result = true; + break; + } + } + + return result; +} + +/////////////////// GUI ////////////////////////// + +function loadSprites():void { + PIXI.loader + .add("playBtn", "images/playSmBtn.png") + .add("stopBtn", "images/stopSmBtn.png") + .add("resetBtn", "images/resetSmBtn.png") + .add("shuffleBtn", "images/shuffleSmBtn.png") + .add("embiggenBtn", "images/embiggenSmBtn.png") + .add("unbiggenBtn", "images/unbiggenSmBtn.png") + .load( + (loader, resources) => { + for (let prop in resources) { + if (resources[prop].error) { + console.log("Had a loading error: " + resources[prop].error); + continue; + } + sprites[prop] = new PIXI.Sprite(resources[prop].texture); + } + layoutUI(); + } + ) +} + +function makeButton(name:string, x:number, y:number):PIXI.Sprite { + let sprite:PIXI.Sprite = sprites[name]; + sprite.name = name; + sprite.position.x = x; + sprite.position.y = y; + sprite.pivot = new PIXI.Point(sprite.width/2, sprite.height/2); + sprite.interactive = true; + sprite.buttonMode = true; + sprite.on("pointerdown", handleButtonPress); + sprite.on("pointerup", handleButtonRelease); + sprite.on("pointerupoutside", handleButtonRelease); + return sprite; +} + +function layoutUI():void { + const MarginLeft:number = 256; + const MarginTop:number = 125; + const ButtonOffset:number = 90; + + statusField.x = 48; + statusField.y = 640; + stage.addChild(statusField); + + buttons["play"] = stage.addChild(makeButton("playBtn", MarginLeft, MarginTop)) as PIXI.Sprite; + buttons["stop"] = stage.addChild(makeButton("stopBtn", MarginLeft, MarginTop + 1 * ButtonOffset)) as PIXI.Sprite; + buttons["reset"] = stage.addChild(makeButton("resetBtn", MarginLeft, MarginTop + 2 * ButtonOffset)) as PIXI.Sprite; + buttons["shuffle"] = stage.addChild(makeButton("shuffleBtn", MarginLeft, MarginTop + 3 * ButtonOffset)) as PIXI.Sprite; + buttons["embiggen"] = stage.addChild(makeButton("embiggenBtn", MarginLeft, MarginTop + 4 * ButtonOffset)) as PIXI.Sprite; + buttons["unbiggen"] = stage.addChild(makeButton("unbiggenBtn", MarginLeft, MarginTop + 5 * ButtonOffset)) as PIXI.Sprite; + + updateCells(); + updateUI(); + + renderer.render(stage); +} + +function updateUI():void { + const Opaque:number = 1.0; + const Dimmed:number = 0.5; + + let isRunning:boolean = GameState.Running == gameState; + let isStopped:boolean = GameState.Stopped == gameState; + + buttons["play"].alpha = Opaque; + buttons["stop"].alpha = isStopped ? Dimmed : Opaque; + buttons["stop"].alpha = isRunning ? Opaque : Dimmed; + buttons["reset"].alpha = !board.roundOver && isStopped ? Dimmed : Opaque; + + buttons["embiggen"].alpha = + buttons["unbiggen"].alpha = + buttons["shuffle"].alpha = isStopped ? Opaque : Dimmed; + if (maxBoardSize == board.numRows) { + buttons["embiggen"].alpha = Dimmed; + } + if (minBoardSize == board.numRows) { + buttons["unbiggen"].alpha = Dimmed; + } +} + +//////////////// Audio /////////////////////////// + +function loadAudio():void { + let audio = new Audio("sounds/tbone.ogg"); + audio.volume = 0.75; + sounds["tbone"] = audio; + + audio = new Audio("sounds/wetfoot.ogg"); + audio.volume = 0.5; + sounds["wetfoot"] = audio; + + audio = new Audio("sounds/click.ogg"); + audio.volume = 1.0; + sounds["click"] = audio; + + audio = new Audio("sounds/tada.ogg"); + audio.volume = 1.0; + sounds["tada"] = audio; +} + +///////////// Drawing //////////////////////////// + +function initBoard(board:Checkerboard):void { + let boardWidth:number = board.numColumns * CellSize; + let boardHeight:number = board.numRows * CellSize; + let surface = new PIXI.Graphics(); + surface.lineStyle(1, 0xffffff, 1); // 16777215 + surface.beginFill(0x666666, 1); + theBigBoard.addChild(surface); + theBigBoard.position.set(spine - boardWidth/2, waistLine - boardHeight/2); + + stage.addChild(theBigBoard); +} + +function updateCells():void { + const normalCellColor:number = 0x666666; // 13421772 + const occupiedCellColor:number = 0xcc33cc; // 13382604 + const visitedCellColor:number = 0xcccc66; // 13421670 + + let penX:number = 0; + let penY:number = 0; + let boardGraphics = theBigBoard.getChildAt(0) as PIXI.Graphics; + + for (let row:number=0; row minBoardSize) { + numRows--; + board = new Checkerboard(numRows, numRows); + initBoard(board); + } +} + +function handleButtonPress(e):void { + let target:PIXI.Sprite = e.target as PIXI.Sprite; + + // low-budget enabled flag + if (target.alpha < 1.0) { + return; + } + switch (target.name) { + case "playBtn": + switch (gameState) { + case GameState.Running: + console.log("no playing during gameplay"); + return; + case GameState.Stopped: + board.reset(); + board.seedPath(); + // now fall through. still legal? + case GameState.Paused: + gameState = GameState.Running; + break; + default: + console.log("You should never this fnord"); + } + break; + case "stopBtn": + if (GameState.Running != gameState) { + console.log("no stopping unless gameplay"); + return; + } + gameState = GameState.Paused; + break; + case "resetBtn": + gameState = GameState.Stopped; + board.reset(); + drawBoard(); + break; + case "shuffleBtn": + if (GameState.Running == gameState) { + console.log("no shuffling during gameplay"); + return; + } + gameState = GameState.Stopped; + (theBigBoard.getChildAt(0) as PIXI.Graphics).clear(); + board.createBoard(board.numRows, board.numColumns); + drawBoard(); + break; + case "embiggenBtn": + (theBigBoard.getChildAt(0) as PIXI.Graphics).clear(); + expandBoard(); + break; + case "unbiggenBtn": + (theBigBoard.getChildAt(0) as PIXI.Graphics).clear(); + shrinkBoard(); + break; + default: + console.log("unhandled click on " + target.name); + return; + } + sounds["click"].play(); + target.scale.x = target.scale.y = 0.90; +} + +function handleButtonRelease(e):void { + let target:PIXI.Sprite = e.target as PIXI.Sprite; + + if (null != target) { + target.scale.x = target.scale.y = 1.0; +// sounds["click"].play(); + } +}