diff --git a/assets/textures/gates/No Input Buffer.png b/assets/textures/gates/No Input Buffer.png new file mode 100644 index 0000000..b525733 Binary files /dev/null and b/assets/textures/gates/No Input Buffer.png differ diff --git a/src/index.js b/src/index.js index dd18d92..29533bf 100644 --- a/src/index.js +++ b/src/index.js @@ -14,8 +14,19 @@ import canvasManager from './objects/canvasManager.js'; */ window.setup = function() { - canvasManager.createCanvas(); - sceneManager.setCurrentScene(MAIN_MENU); + let promises = []; + for (let i of sceneManager + .getScenes() + .sceneIter()) + { + promises = [...promises, ...i.preload()]; + } + Promise + .all(promises) + .then(() => { + canvasManager.createCanvas(); + sceneManager.setCurrentScene(MAIN_MENU); + }); } /** @@ -25,4 +36,4 @@ window.setup = function() window.draw = function() { sceneManager.getCurrentScene().draw(); -} \ No newline at end of file +} diff --git a/src/objects/andGate.js b/src/objects/andGate.js new file mode 100644 index 0000000..97653de --- /dev/null +++ b/src/objects/andGate.js @@ -0,0 +1,14 @@ +import TwoInputGate from "./twoInputGate.js"; + +export default class AndGate extends TwoInputGate +{ + constructor(input1, input2, img) + { + super(input1, input2, img); + } + + answer() + { + return this.input1.answer() & this.input2.answer(); + } +} diff --git a/src/objects/constantGate.js b/src/objects/constantGate.js new file mode 100644 index 0000000..a736e36 --- /dev/null +++ b/src/objects/constantGate.js @@ -0,0 +1,15 @@ +import Gate from "./gate.js"; + +export default class ConstantGate extends Gate +{ + constructor(value, img) + { + super(img); + this.value = value; + } + + answer() + { + return this.value; + } +} diff --git a/src/objects/gate.js b/src/objects/gate.js new file mode 100644 index 0000000..7c255d5 --- /dev/null +++ b/src/objects/gate.js @@ -0,0 +1,36 @@ +import { scaleNearestNeighbor } from "../helperFunctions/imageFunctions.js"; + +export default class Gate +{ + constructor(img) + { + if (this.constructor === Gate) + { + throw new TypeError("Abstract class \"Gate\" cannot be instantiated directly."); + } + this.img = img; + } + + answer() + { + if (this.schema === undefined) + { + throw new TypeError("Classes extending \"Gate\" must implement \"answer()\""); + } + } + + show(x, y) + { + if (this.answer()) + tint("#ffff00"); + image(this.img, x, y); + noTint(); + } + + // This function should be used only occasionally + // Scaling apparently is very intensive on + showScaled(x, y, width, height) + { + image(scaleNearestNeighbor(this.img, width, height), x, y); + } +} diff --git a/src/objects/nandGate.js b/src/objects/nandGate.js new file mode 100644 index 0000000..48f8146 --- /dev/null +++ b/src/objects/nandGate.js @@ -0,0 +1,14 @@ +import { AndGate } from "andGate.js"; + +export class NandGate extends AndGate +{ + constructor(input1, input2, img) + { + super(input1, input2, img); + } + + answer() + { + return !super.answer(); + } +} diff --git a/src/objects/norGate.js b/src/objects/norGate.js new file mode 100644 index 0000000..fa41a0b --- /dev/null +++ b/src/objects/norGate.js @@ -0,0 +1,14 @@ +import { OrGate } from "orGate.js"; + +export class NorGate extends OrGate +{ + constructor(input1, input2, img) + { + super(input1, input2, img); + } + + answer() + { + return !super.answer(); + } +} diff --git a/src/objects/notGate.js b/src/objects/notGate.js new file mode 100644 index 0000000..6fec01e --- /dev/null +++ b/src/objects/notGate.js @@ -0,0 +1,14 @@ +import { OneInputGate } from "OneInputGate.js"; + +export class NotGate extends OneInputGate +{ + constructor(input, img) + { + super(input, img); + } + + answer() + { + return !this.input1.answer(); + } +} diff --git a/src/objects/oneInputGate.js b/src/objects/oneInputGate.js new file mode 100644 index 0000000..c1353d8 --- /dev/null +++ b/src/objects/oneInputGate.js @@ -0,0 +1,14 @@ +import Gate from "./gate.js"; + +export default class OneInputGate extends Gate +{ + constructor(input, img) + { + super(img); + if (this.constructor === OneInputGate) + { + throw new TypeError("Abstract class \"OneInputGate\" cannot be instantiated directly."); + } + this.input1 = input; + } +} diff --git a/src/objects/orGate.js b/src/objects/orGate.js new file mode 100644 index 0000000..d8134f7 --- /dev/null +++ b/src/objects/orGate.js @@ -0,0 +1,14 @@ +import { TwoInputGate } from "twoInputGate.js"; + +export class OrGate extends TwoInputGate +{ + constructor(input1, input2, img) + { + super(input1, input2, img); + } + + answer() + { + return this.input1.answer() | this.input2.answer(); + } +} diff --git a/src/objects/scene.js b/src/objects/scene.js index 6f975ae..bf9e0bc 100644 --- a/src/objects/scene.js +++ b/src/objects/scene.js @@ -4,8 +4,11 @@ * we never want to instantiate this class directly. */ export default class Scene { + preload() { + return []; + } setup() {} draw() { background(0, 0, 0); } -} \ No newline at end of file +} diff --git a/src/objects/sceneManager.js b/src/objects/sceneManager.js index 695aafa..b7864c2 100644 --- a/src/objects/sceneManager.js +++ b/src/objects/sceneManager.js @@ -27,7 +27,14 @@ class SceneManager { [GAME_OVER]: new GameOver(), [INSTRUCTIONS]: new Instructions(), [PRACTICE_MODE]: new PracticeMode(), - [SETTINGS]: new Settings() + [SETTINGS]: new Settings(), + sceneIter: function* () + { + for (let i of sceneManager.getValidScenes()) + { + yield this[i]; + } + } }; #valid_scenes = [ @@ -60,9 +67,19 @@ class SceneManager { this.#current_scene = sceneName; this.getCurrentScene().setup(); } + + getScenes() + { + return this.#scenes; + } + + getValidScenes() + { + return this.#valid_scenes; + } } // We only ever want one instance of this class (a singleton), so we will instantiate it here and then export that single instantiation. const sceneManager = new SceneManager(); -export default sceneManager; \ No newline at end of file +export default sceneManager; diff --git a/src/objects/twoInputGate.js b/src/objects/twoInputGate.js new file mode 100644 index 0000000..2740d81 --- /dev/null +++ b/src/objects/twoInputGate.js @@ -0,0 +1,16 @@ +import OneInputGate from "./oneInputGate.js"; + +export default class TwoInputGate extends OneInputGate +{ + input2; + + constructor(input1, input2, img) + { + super(input1, img); + if (this.constructor === TwoInputGate) + { + throw new TypeError("Abstract class \"TwoInputGate\" cannot be instantiated directly."); + } + this.input2 = input2; + } +} diff --git a/src/objects/xnorGate.js b/src/objects/xnorGate.js new file mode 100644 index 0000000..e83f166 --- /dev/null +++ b/src/objects/xnorGate.js @@ -0,0 +1,14 @@ +import { XorGate } from "xorGate.js"; + +export class XnorGate extends XorGate +{ + constructor(input1, input2, img) + { + super(input1, input2, img); + } + + answer() + { + return !super.answer(); + } +} diff --git a/src/objects/xorGate.js b/src/objects/xorGate.js new file mode 100644 index 0000000..753728d --- /dev/null +++ b/src/objects/xorGate.js @@ -0,0 +1,14 @@ +import { TwoInputGate } from "twoInputGate.js"; + +export class XorGate extends TwoInputGate +{ + constructor(input1, input2, img) + { + super(input1, input2, img); + } + + answer() + { + return this.input1.answer() ^ this.input2.answer(); + } +} diff --git a/src/scenes/challengeMode.js b/src/scenes/challengeMode.js index 1b03fe3..dfd451d 100644 --- a/src/scenes/challengeMode.js +++ b/src/scenes/challengeMode.js @@ -1,4 +1,6 @@ import Scene from "../objects/scene.js"; +import ConstantGate from "../objects/constantGate.js"; +import AndGate from "../objects/andGate.js"; /** * This scene will manage the game's challenge mode. @@ -6,10 +8,68 @@ import Scene from "../objects/scene.js"; export default class ChallengeMode extends Scene { constructor() { super(); + this.bufferImg = null; + this.notImg = null; + this.andImg = null; + this.orImg = null; + this.xorImg = null; + this.nandImg = null; + this.norImg = null; + this.xnorImg = null; + this.constant1 = null; + this.constant2 = null; + this.gate = null; } - setup() {} + preload() + { + let promises = [ + new Promise((resolve, reject) => { + this.bufferImg = loadImage("assets/textures/gates/No Input Buffer.png"); + resolve(true); + }), + new Promise((resolve, reject) => { + this.notImg = loadImage("assets/textures/gates/NOT Gate.png"); + resolve(true); + }), + new Promise((resolve, reject) => { + this.andImg = loadImage("assets/textures/gates/AND Gate.png"); + resolve(true); + }), + new Promise((resolve, reject) => { + this.orImg = loadImage("assets/textures/gates/OR Gate.png"); + resolve(true); + }), + new Promise((resolve, reject) => { + this.xorImg = loadImage("assets/textures/gates/XOR Gate.png"); + resolve(true); + }), + new Promise((resolve, reject) => { + this.nandImg = loadImage("assets/textures/gates/NAND Gate.png"); + resolve(true); + }), + new Promise((resolve, reject) => { + this.norImg = loadImage("assets/textures/gates/NOR Gate.png"); + resolve(true); + }), + new Promise((resolve, reject) => { + this.xnorImg = loadImage("assets/textures/gates/XNOR Gate.png"); + resolve(true); + }) + ]; + return promises; + } + + setup() { + this.constant1 = new ConstantGate(1, this.bufferImg); + this.constant2 = new ConstantGate(1, this.bufferImg); + this.gate = new AndGate(this.constant1, this.constant2, this.andImg); + } + draw() { background(0, 0, 255); + this.constant1.show(100, 100); + this.constant2.show(100, 200); + this.gate.show(150, 150); } -} \ No newline at end of file +} diff --git a/src/scenes/cheatSheet.js b/src/scenes/cheatSheet.js index 616eccc..cd287ba 100644 --- a/src/scenes/cheatSheet.js +++ b/src/scenes/cheatSheet.js @@ -13,24 +13,34 @@ export default class CheatSheet extends Scene { this.cheatSheetBtn = {}; } - setup() { - this.cheatSheetImg = loadImage("assets/textures/CheatSheet.png", - () => this.cheatSheetImg = scaleNearestNeighbor(this.cheatSheetImg, 640, 480)); - this.cheatSheetBtnImg = loadImage("assets/textures/CheatSheetBackButton.png", - () => { - this.cheatSheetBtnImg = scaleNearestNeighbor(this.cheatSheetBtnImg, 301, 103); - this.cheatSheetBtn = new ImageButton( - this.cheatSheetBtnImg, - 13, - 359, - () => { - canvasManager.canvas.clear(); - sceneManager.setCurrentScene(MAIN_MENU); - } - ); - }); + preload() + { + let promiseA = new Promise((resolve, reject) => + { + this.cheatSheetImg = loadImage("assets/textures/CheatSheet.png", + () => this.cheatSheetImg = scaleNearestNeighbor(this.cheatSheetImg, 640, 480)); + resolve(true); + }); + let promiseB = new Promise((resolve, reject) => + { + this.cheatSheetBtnImg = loadImage("assets/textures/CheatSheetBackButton.png", + () => this.cheatSheetBtnImg = scaleNearestNeighbor(this.cheatSheetBtnImg, 301, 103)); + resolve(true); + }); + return [promiseA, promiseB]; + } - canvasManager.canvas.mouseClicked((event) => { + setup() { + this.cheatSheetBtn = new ImageButton( + this.cheatSheetBtnImg, + 13, + 359, + () => { + canvasManager.canvas.clear(); + sceneManager.setCurrentScene(MAIN_MENU); + } + ); + canvasManager.canvas.mouseClicked((event) => { this.cheatSheetBtn.callback(event); }); } @@ -39,4 +49,4 @@ export default class CheatSheet extends Scene { image(this.cheatSheetImg, 0, 0); image(this.cheatSheetBtnImg, 13, 359); } -} \ No newline at end of file +} diff --git a/src/scenes/gameOver.js b/src/scenes/gameOver.js index 7b9b399..8e49600 100644 --- a/src/scenes/gameOver.js +++ b/src/scenes/gameOver.js @@ -12,4 +12,4 @@ export default class GameOver extends Scene { draw() { background(0, 255, 255); } -} \ No newline at end of file +} diff --git a/src/scenes/instructions.js b/src/scenes/instructions.js index 31f8a4d..bda3b5d 100644 --- a/src/scenes/instructions.js +++ b/src/scenes/instructions.js @@ -12,4 +12,4 @@ export default class Instructions extends Scene { draw() { background(255, 0, 0); } -} \ No newline at end of file +} diff --git a/src/scenes/mainMenu.js b/src/scenes/mainMenu.js index 83cfe51..3043fcd 100644 --- a/src/scenes/mainMenu.js +++ b/src/scenes/mainMenu.js @@ -9,6 +9,15 @@ export default class MainMenu extends Scene { constructor() { super(); this.imageButtons = []; + this.btnImgs = []; + this.img = null; + this.btnScenes = [ + CHALLENGE_MODE, + PRACTICE_MODE, + INSTRUCTIONS, + CHEAT_SHEET, + SETTINGS + ]; } /** @@ -20,35 +29,38 @@ export default class MainMenu extends Scene { sceneManager.setCurrentScene(sceneName); } + preload() + { + const btnImageSpriteSheet = "assets/textures/mainMenu/buttons.png"; + let promise = new Promise((resolve, reject) => { + this.img = loadImage(btnImageSpriteSheet, () => + { + this.img.loadPixels(); + this.btnScenes.forEach((e, i, a) => { + let subImg = this.img.get(0, i*11, 75, 11); + this.btnImgs.push(scaleNearestNeighbor(subImg, 480, 70)); + }); + resolve(true); + }) + }); + return [promise]; + } + setup() { - const btnImageSpritesheet = "assets/textures/mainMenu/buttons.png"; - const btnScenes = [ - CHALLENGE_MODE, - PRACTICE_MODE, - INSTRUCTIONS, - CHEAT_SHEET, - SETTINGS - ]; - let btnImgs = []; - let img = loadImage(btnImageSpritesheet, () => { - img.loadPixels(); - btnScenes.forEach((e, i, a) => { - let subImg = img.get(0, i*11, 75, 11); - btnImgs.push(scaleNearestNeighbor(subImg, 480, 70)); - }); - btnImgs.forEach((image, index) => { - let btn = new ImageButton( - image, // The image to set - 0, // The x coordinate - index * 70, // The y coordinate - () => this.#changeScene(btnScenes[index]) // The function to call when this button is clicked on - ); - this.imageButtons.push(btn); - }); + + this.btnImgs.forEach((image, index) => { + let btn = new ImageButton( + image, // The image to set + 0, // The x coordinate + index * 70, // The y coordinate + () => this.#changeScene(this.btnScenes[index]) // The function to call when this button is clicked on + ); + this.imageButtons.push(btn); }); canvasManager.canvas.mouseClicked((event) => { - this.imageButtons.forEach(button => button.callback(event)); + for (let i of this.imageButtons) + i.callback(event); }); } diff --git a/src/scenes/practiceMode.js b/src/scenes/practiceMode.js index 7099869..860dd5b 100644 --- a/src/scenes/practiceMode.js +++ b/src/scenes/practiceMode.js @@ -12,4 +12,4 @@ export default class PracticeMode extends Scene { draw() { background(255, 255, 0); } -} \ No newline at end of file +} diff --git a/src/scenes/settings.js b/src/scenes/settings.js index e9b5d7f..a973041 100644 --- a/src/scenes/settings.js +++ b/src/scenes/settings.js @@ -12,4 +12,4 @@ export default class Settings extends Scene { draw() { background(123, 123, 123); } -} \ No newline at end of file +}