diff --git a/index.html b/index.html index e6584f1..3901b9b 100644 --- a/index.html +++ b/index.html @@ -10,11 +10,17 @@ canvas { background: grey } + p { + display: inline + } + + +

Counter: 0 Cells alive: 0 Time to handle: 0

diff --git a/main.ts b/main.ts index 3f3a986..5389f84 100644 --- a/main.ts +++ b/main.ts @@ -1,175 +1,4 @@ -class GOF { - canvas: HTMLCanvasElement - ctx: CanvasRenderingContext2D - - cellSize: number - width: number - height: number - - alive: Cell[] = [] - public constructor(canvas: HTMLCanvasElement, width: number = 100, height: number = 100, size: number = 10) { - this.ctx = canvas.getContext("2d") - this.canvas = canvas - this.width = width - this.canvas.width = width - this.height = height - this.canvas.height = height - this.cellSize = size - console.log(this.alive) - } - - public addCell(x: number, y: number): Cell { - let cell = new Cell(x, y, this.cellSize, this, true) - this.alive.push(cell) - return cell - } - - public getCellAt(x: number, y: number): Cell { - return (this.alive as any).find((el: Cell) => {return el.x == x && el.y == y}) - } - - public update() { - let cellsToCheck: Cell[] = [] - - this.ctx.clearRect(0,0,this.width,this.height) - this.alive.forEach(cell => { - cellsToCheck.push(...cell.getDeadNeighbour()) - // console.log(...cell.getDeadNeighbour()) - }) - cellsToCheck.push(...this.alive) - // console.log(cellsToCheck) - let tAlive: Cell [] = [] - cellsToCheck.forEach((cell: Cell) => { - cell.prepareNextTurn() - if ( - cell.ntValue == true && - !(tAlive as any).find((el: Cell) => {return el.x == cell.x && el.y == cell.y}) - ) { - tAlive.push(cell) - } - }) - this.alive = tAlive - - this.alive.forEach(cell => { - cell.draw() - }) - this.ctx.fillStyle = "black" - this.ctx.beginPath() - for (let index = 0; index < this.height; index += this.cellSize) { - this.ctx.moveTo(0, index) - this.ctx.lineTo(this.width, index) - this.ctx.stroke() - } - for (let index = 0; index < this.width; index += this.cellSize) { - this.ctx.moveTo(index, 0) - this.ctx.lineTo(index, this.height) - this.ctx.stroke() - } - this.ctx.closePath() - } -} - -class Cell { - - // public constructor(x:number, y: number, size: number, engine: GOF) { - // this.x = x - // this.y = y - // this.size = size - // this.engine = engine - // } - - public constructor(x:number, y: number, size: number, engine: GOF, value: boolean = false) { - this.x = x - this.y = y - this.size = size - this.engine = engine - this.value = value - } - - - private color: string = "yellow" - - private _engine: GOF - public set engine(v: GOF) { - this._engine = v - } - public get engine(): GOF { - return this._engine - } - - private _x: number - public set x(v: number) { - this._x = v - } - public get x(): number { - return this._x - } - - private _y: number - public set y(v: number) { - this._y = v - } - public get y(): number { - return this._y - } - - private _size: number - public set size(v: number) { - this._size = v - } - public get size(): number { - return this._size - } - - - private _value: boolean = false - public set value(v: boolean) { - this._value = v - } - public get value(): boolean { - return this._value - } - - private _ntValue: boolean - public set ntValue(v: boolean) { - this._ntValue = v - } - public get ntValue(): boolean { - return this._ntValue - } - - - public getDeadNeighbour() { - let nb: Cell[] = [] - for (let i = -1; i <= 1; i++) { - for (let j = -1; j <= 1; j++) { - let cell = this.engine.getCellAt(this.x+i, this.y+j) - if (cell === undefined) { - nb.push(new Cell(this.x+i, this.y+j, this.size, this.engine)) - continue - } - } - } - return nb - } - - public draw() { - if (this.ntValue) this.value = true - this.ntValue = undefined - this.engine.ctx.fillStyle = this.color - this.engine.ctx.fillRect(this.x*this.size,this.y*this.size,this.size,this.size) - // this.engine.ctx.fillStyle = "black" - // this.engine.ctx.font = "30px Roboto" - // this.engine.ctx.fillText(this.getDeadNeighbour().length + "", this.x*this.size+this.size/2.5,this.y*this.size+this.size/1.5) - } - - public prepareNextTurn() { - let nLength = this.getDeadNeighbour().length - if (!this.value && nLength == 6) this.ntValue = true - if (this.value && (nLength > 6 || nLength < 5)) this.ntValue = false - if (this.ntValue === undefined) this.ntValue = this.value - } -} +import GOF from './src/GOF' /* go to each alive cells @@ -185,18 +14,35 @@ For a space that is 'empty' or 'unpopulated' Each cell with three neighbors becomes populated. 6 dead */ +let canvasWidth = 1000 +let cellNumber = 50 +let cellSize = canvasWidth/cellNumber const c: HTMLCanvasElement = document.querySelector("canvas") -let gof = new GOF(c, 900, 900, 900/100); + +c.addEventListener("mousedown", (e) => { + const rect = c.getBoundingClientRect() + const x = (Math as any).trunc((e.clientX - rect.left)/cellSize) + const y = (Math as any).trunc((e.clientY - rect.top)/cellSize) + console.log("x: " + x + " y: " + y) + let tCell = gof.getCellAt(x, y) + if (tCell !== undefined) { + tCell.value = false + tCell.ntValue = false + } + else tCell = gof.addCell(x, y) + tCell.draw() +}) + +let gof = new GOF(c, canvasWidth, canvasWidth, cellSize); (window as any).gof = gof gof.update() // gof.addCell(3,4) // let cell = gof.addCell(4,4) -gof.addCell(5,4) -for (let k = 0; k < 100; k++) { - gof.addCell(k, 100/2) +// for (let k = 0; k < cellNumber; k++) { +// gof.addCell(k, cellNumber/2) -} +// } gof.alive.forEach(el => { el.draw() }) @@ -205,11 +51,27 @@ gof.alive.forEach(el => { let interval: number document.querySelector(".start").addEventListener("click", () => { - interval = setInterval(() => { + if (interval === undefined) interval = setInterval(() => { + let start = new Date().getTime() gof.update() - }, 10) + let speed = document.querySelector(".speed") + speed.innerHTML = new Date().getTime() - start + "" + }, 1) }) document.querySelector(".pause").addEventListener("click", () => { - clearInterval(interval) + if (interval != undefined) clearInterval(interval) + interval = undefined +}) + +document.querySelector(".grid").addEventListener("click", () => { + gof.showGrid = !gof.showGrid +}) + +document.querySelector(".step").addEventListener("click", () => { + let start = new Date().getTime() + gof.update() + let speed = document.querySelector(".speed") + speed.innerHTML = new Date().getTime() - start + "" + }) diff --git a/src/Cell.ts b/src/Cell.ts new file mode 100644 index 0000000..380891a --- /dev/null +++ b/src/Cell.ts @@ -0,0 +1,99 @@ +import GOF from './GOF' + +export default class Cell { + + public constructor(x:number, y: number, size: number, engine: GOF, value: boolean = false) { + this.x = x + this.y = y + this.size = size + this.engine = engine + this.value = value + } + + + private color: string = "yellow" + + private _engine: GOF + public set engine(v: GOF) { + this._engine = v + } + public get engine(): GOF { + return this._engine + } + + private _x: number + public set x(v: number) { + this._x = v + } + public get x(): number { + return this._x + } + + private _y: number + public set y(v: number) { + this._y = v + } + public get y(): number { + return this._y + } + + private _size: number + public set size(v: number) { + this._size = v + } + public get size(): number { + return this._size + } + + + private _value: boolean = false + public set value(v: boolean) { + this._value = v + } + public get value(): boolean { + return this._value + } + + private _ntValue: boolean + public set ntValue(v: boolean) { + this._ntValue = v + } + public get ntValue(): boolean { + return this._ntValue + } + + + public getDeadNeighbour() { + let nb: Cell[] = [] + for (let i = -1; i <= 1; i++) { + for (let j = -1; j <= 1; j++) { + let cell = this.engine.getCellAt(this.x+i, this.y+j) + if (cell === undefined) { + nb.push(new Cell(this.x+i, this.y+j, this.size, this.engine)) + continue + } + } + } + return nb + } + + public draw(draw: boolean = true) { + if (this.ntValue) this.value = true + this.ntValue = undefined + if (draw) { + this.engine.ctx.fillStyle = this.color + if (this.value)this.engine.ctx.fillRect(this.x*this.size,this.y*this.size,this.size,this.size) + else this.engine.ctx.clearRect(this.x*this.size,this.y*this.size,this.size,this.size) + } + // this.engine.ctx.fillStyle = "black" + // this.engine.ctx.font = "30px Roboto" + // this.engine.ctx.fillText(this.getDeadNeighbour().length + "", this.x*this.size+this.size/2.5,this.y*this.size+this.size/1.5) + } + + public prepareNextTurn() { + let nLength = this.getDeadNeighbour().length + if (!this.value && nLength == 6) this.ntValue = true + if (this.value && (nLength > 6 || nLength < 5)) this.ntValue = false + if (this.ntValue === undefined) this.ntValue = this.value + } +} diff --git a/src/GOF.ts b/src/GOF.ts new file mode 100644 index 0000000..d34b302 --- /dev/null +++ b/src/GOF.ts @@ -0,0 +1,106 @@ +import Cell from './Cell' + +interface keyVal { + [key: string]: Cell +} + +export default class GOF { + canvas: HTMLCanvasElement + ctx: CanvasRenderingContext2D + + cellSize: number + width: number + height: number + + alive: Cell[] = [] + + showGrid: boolean = true + public constructor(canvas: HTMLCanvasElement, width: number = 100, height: number = 100, size: number = 10) { + this.ctx = canvas.getContext("2d") + this.canvas = canvas + this.width = width + this.canvas.width = width + this.height = height + this.canvas.height = height + this.cellSize = size + console.log(this.alive) + } + + public addCell(x: number, y: number): Cell { + let cell = new Cell(x, y, this.cellSize, this, true) + this.alive.push(cell) + return cell + } + + public getCellAt(x: number, y: number): Cell { + return (this.alive as any).find((el: Cell) => {return el.x == x && el.y == y}) + } + + public removeDups(cells: Cell[]): Cell[] { + let temp: keyVal = {} + for (const cell of cells) { + temp[`${cell.x}:${cell.y}`] = cell + } + let arr: Cell[] = [] + for (const key in temp) { + if (temp.hasOwnProperty(key)) { + const cell = temp[key]; + arr.push(cell) + } + } + return arr + } + + public update() { + let counter = document.querySelector(".counter") + counter.innerHTML = parseInt(counter.innerHTML)+1 + "" + let cellsToCheck: Cell[] = [] + + this.ctx.clearRect(0,0,this.width,this.height) + this.alive.forEach(cell => { + cellsToCheck.push(...cell.getDeadNeighbour()) + // console.log(...cell.getDeadNeighbour()) + }) + cellsToCheck.push(...this.alive) + cellsToCheck = this.removeDups(cellsToCheck) + // console.log(cellsToCheck) + let tAlive: Cell [] = [] + cellsToCheck.forEach((cell: Cell) => { + cell.prepareNextTurn() + if ( + cell.ntValue == true && + // cell.x <= this.width/this.cellSize && + // cell.y <= this.height/this.cellSize && + !(tAlive as any).find((el: Cell) => {return el.x == cell.x && el.y == cell.y}) + ) { + tAlive.push(cell) + } + }) + this.alive = tAlive + let count = document.querySelector(".cell-count") + count.innerHTML = this.alive.length + "" + + this.alive.forEach(cell => { + let toDraw = true + if (cell.y > this.height/this.cellSize || cell.y < 0) toDraw = false + if (cell.x > this.width/this.cellSize || cell.x < 0) toDraw = false + cell.draw(toDraw) + }) + if (this.showGrid) { + this.ctx.fillStyle = "black" + this.ctx.beginPath() + for (let index = 0; index < this.height; index += this.cellSize) { + this.ctx.moveTo(0, index) + this.ctx.lineTo(this.width, index) + this.ctx.stroke() + } + for (let index = 0; index < this.width; index += this.cellSize) { + this.ctx.moveTo(index, 0) + this.ctx.lineTo(index, this.height) + this.ctx.stroke() + } + this.ctx.closePath() + } + + } +}