Commit 3edd91a9 authored by Adrian Kosmaczewski's avatar Adrian Kosmaczewski

Changed TypeScript tests from tape to mocha; added eslint validation

parent c5657938
module.exports = {
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'standard'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 12,
sourceType: 'module'
},
plugins: [
'@typescript-eslint'
],
rules: {
}
}
......@@ -2,39 +2,39 @@ import browserify from 'browserify'
import gulp from 'gulp'
import uglify from 'gulp-uglify'
const tsify = require('tsify')
const source = require("vinyl-source-stream")
const source = require('vinyl-source-stream')
const buffer = require('vinyl-buffer')
const chmod = require('gulp-chmod')
const insert = require('gulp-insert')
exports.default = gulp.task(
"default",
'default',
gulp.series(function () {
return browserify({
entries: ["src/main_browser.ts"]
entries: ['src/main_browser.ts']
})
.plugin(tsify)
.bundle()
.pipe(source("conway.js"))
.pipe(source('conway.js'))
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest("dist"))
.pipe(gulp.dest('dist'))
})
)
exports.conway = gulp.task(
"conway",
'conway',
gulp.series(function () {
return browserify({
entries: ["src/main_cli.ts"]
entries: ['src/main_cli.ts']
})
.plugin(tsify)
.bundle()
.pipe(source("conway"))
.pipe(source('conway'))
.pipe(buffer())
.pipe(uglify())
.pipe(insert.prepend('#!/usr/bin/env node\n'))
.pipe(chmod(0o755))
.pipe(gulp.dest("dist"))
.pipe(gulp.dest('dist'))
})
)
This diff is collapsed.
......@@ -4,28 +4,38 @@
"description": "",
"main": "index.js",
"scripts": {
"build-browser": "node_modules/.bin/gulp",
"build-cli": "node_modules/.bin/gulp conway",
"test": "node -r ts-node/register tests/* | node_modules/.bin/tap-spec",
"clean": "node_modules/.bin/rimraf dist"
"build-browser": "npx gulp",
"build-cli": "npx gulp conway",
"test": "npx ts-mocha spec/*",
"lint": "npx eslint src/**/*.ts",
"clean": "npx rimraf dist"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/browserify": "^12.0.36",
"@types/gulp": "^4.0.6",
"@types/chai": "^4.2.13",
"@types/gulp": "^4.0.7",
"@types/gulp-uglify": "^3.0.6",
"@types/mocha": "^8.0.3",
"@types/node": "^13.13.6",
"@types/tape": "^4.13.0",
"@typescript-eslint/eslint-plugin": "^4.4.1",
"@typescript-eslint/parser": "^4.4.1",
"browserify": "^16.5.1",
"chai": "^4.2.0",
"eslint": "^7.11.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"gulp": "^4.0.2",
"gulp-chmod": "^3.0.0",
"gulp-insert": "^0.5.0",
"gulp-uglify": "^3.0.2",
"mocha": "^8.1.3",
"rimraf": "^3.0.2",
"tap-spec": "^5.0.0",
"tape": "^4.13.2",
"ts-loader": "^4.5.0",
"ts-mocha": "^7.0.0",
"ts-node": "^8.10.1",
"tsify": "^4.0.1",
"typescript": "^3.9.2",
......
import { World } from '../src/World'
import { Coord } from '../src/Coord'
import { describe } from 'mocha'
import { expect } from 'chai'
describe('Block', () => {
it('Evolves', () => {
let alive = World.block(new Coord(0, 0))
let original = new World(5, alive)
let next = original.evolve()
expect(JSON.stringify(next.cells)).to.equal(JSON.stringify(original.cells))
})
})
describe('Tub', () => {
it('Evolves', () => {
let alive = World.tub(new Coord(0, 0))
let original = new World(5, alive)
let next = original.evolve()
expect(JSON.stringify(next.cells)).to.equal(JSON.stringify(original.cells))
})
})
describe('Blinker', () => {
it('Evolves', () => {
let alive = World.blinker(new Coord(0, 1))
let original = new World(3, alive)
let gen1 = original.evolve()
let expectedAlive: Coord[] = [
new Coord(1, 0),
new Coord(1, 1),
new Coord(1, 2)
];
let expected = new World(3, expectedAlive)
expect(JSON.stringify(expected.cells)).to.equal(JSON.stringify(gen1.cells))
let gen2 = gen1.evolve()
expect(JSON.stringify(original.cells)).to.equal(JSON.stringify(gen2.cells))
})
})
/* eslint-disable no-unused-vars */
export enum Cell {
alive,
dead
}
export function cellToString(cell: Cell | undefined): string {
return cell == Cell.alive ? " x |" : " |";
export function cellToString (cell: Cell | undefined): string {
return cell === Cell.alive ? ' x |' : ' |'
}
/* eslint-disable no-useless-constructor */
export class Coord {
constructor(public x: number, public y: number) { }
constructor (public x: number, public y: number) { }
toString(): string {
toString (): string {
return `${this.x}:${this.y}`
}
static fromString(string: string): Coord {
let parts: string[] = string.split(":")
let c: number[] = parts.map(parseFloat)
static fromString (string: string): Coord {
const parts: string[] = string.split(':')
const c: number[] = parts.map(parseFloat)
return new Coord(c[0], c[1])
}
}
import {Cell, cellToString} from "./Cell"
import {Coord} from "./Coord"
import { Cell, cellToString } from './Cell'
import { Coord } from './Coord'
// Adapted from
// https://stackoverflow.com/a/14760377/133764
let paddingLeft = (value: number, paddingValue: string): string => {
const paddingLeft = (value: number, paddingValue: string): string => {
return String(paddingValue + value).slice(-paddingValue.length)
}
// Courtesy of
// https://stackoverflow.com/a/10050831/133764
let makeRange = (size: number): number[] => {
const makeRange = (size: number): number[] => {
return Array.apply(null, Array(size)).map((_, i) => { return i })
}
export class World {
cells = new Map<string, Cell>();
constructor(public size: number, aliveCells: Array<Coord>) {
constructor (public size: number, aliveCells: Array<Coord>) {
this.size = size
let aliveCellStrings : string[] = aliveCells.map(item => item.toString())
let range = makeRange(size)
for (let a of range) {
for (let b of range) {
let coord = new Coord(a, b)
const aliveCellStrings : string[] = aliveCells.map(item => item.toString())
const range = makeRange(size)
for (const a of range) {
for (const b of range) {
const coord = new Coord(a, b)
if (aliveCellStrings.includes(coord.toString())) {
this.cells.set(coord.toString(), Cell.alive)
}
else {
} else {
this.cells.set(coord.toString(), Cell.dead)
}
}
}
}
evolve(): World {
let alive = Array<Coord>()
let range = [-1, 0, 1]
evolve (): World {
const alive : Coord[] = []
const range = [-1, 0, 1]
this.cells.forEach((cell, coordText) => {
let coord = Coord.fromString(coordText)
const coord = Coord.fromString(coordText)
let count = 0
range.map(a => {
count += range.map(b => {
return (new Coord(coord.x + a, coord.y + b)).toString()
}).filter(c => {
return c != coordText
return c !== coordText
}).map(c => {
return this.cells.get(c)
}).filter(o => {
return o == Cell.alive
return o === Cell.alive
}).length
})
switch (cell) {
case Cell.alive: {
if (count == 2 || count == 3) {
if (count === 2 || count === 3) {
alive.push(coord)
}
break
}
case Cell.dead: {
if (count == 3) {
if (count === 3) {
alive.push(coord)
}
break
......@@ -70,32 +69,32 @@ export class World {
return new World(this.size, alive)
}
toString(): string {
let str = ""
let range = makeRange(this.size)
toString (): string {
let str = ''
const range = makeRange(this.size)
for (let a of range) {
if (a == 0) {
for (const a of range) {
if (a === 0) {
// First line with coordinates
str += " "
str += ' '
for (let b = 0; b < this.size; ++b) {
str += ` ${paddingLeft(b, " ")}|`
str += ` ${paddingLeft(b, ' ')}|`
}
str += "\n"
str += '\n'
}
str += ` ${paddingLeft(a, " ")}|`
for (let b of range) {
let coord = new Coord(b, a)
let cell = this.cells.get(coord.toString())
let cellDescription = cellToString(cell)
str += ` ${paddingLeft(a, ' ')}|`
for (const b of range) {
const coord = new Coord(b, a)
const cell = this.cells.get(coord.toString())
const cellDescription = cellToString(cell)
str += `${cellDescription}`
}
str += "\n"
str += '\n'
}
return str
}
static blinker(origin: Coord): Array<Coord> {
static blinker (origin: Coord): Array<Coord> {
return [
new Coord(origin.x, origin.y),
new Coord(origin.x + 1, origin.y),
......@@ -103,7 +102,7 @@ export class World {
]
}
static beacon(origin: Coord): Array<Coord> {
static beacon (origin: Coord): Array<Coord> {
return [
new Coord(origin.x, origin.y),
new Coord(origin.x + 1, origin.y),
......@@ -116,7 +115,7 @@ export class World {
]
}
static glider(origin: Coord): Array<Coord> {
static glider (origin: Coord): Array<Coord> {
return [
new Coord(origin.x + 2, origin.y + 2),
new Coord(origin.x + 1, origin.y + 2),
......@@ -126,7 +125,7 @@ export class World {
]
}
static block(origin: Coord): Array<Coord> {
static block (origin: Coord): Array<Coord> {
return [
new Coord(origin.x, origin.y),
new Coord(origin.x + 1, origin.y),
......@@ -135,7 +134,7 @@ export class World {
]
}
static tub(origin: Coord): Array<Coord> {
static tub (origin: Coord): Array<Coord> {
return [
new Coord(origin.x + 1, origin.y),
new Coord(origin.x, origin.y + 1),
......
import { World } from "./World"
import { Cell } from "./Cell"
import { Coord } from "./Coord"
import { World } from './World'
import { Cell } from './Cell'
import { Coord } from './Coord'
export class WorldView {
context: CanvasRenderingContext2D | null
......@@ -8,24 +8,25 @@ export class WorldView {
contentWidth: number
contentHeight: number
set world(newWorld: World) {
// eslint-disable-next-line accessor-pairs
set world (newWorld: World) {
this.currentWorld = newWorld
this.clear()
this.draw()
}
constructor(private canvas: HTMLCanvasElement) {
this.context = this.canvas.getContext("2d")
constructor (private canvas: HTMLCanvasElement) {
this.context = this.canvas.getContext('2d')
this.currentWorld = new World(30, World.blinker(new Coord(0, 1)))
this.contentWidth = this.canvas.width
this.contentHeight = this.canvas.height
}
clear() {
clear () {
this.context?.clearRect(0, 0, this.contentWidth, this.contentHeight)
}
draw() {
draw () {
if (this.currentWorld && this.context) {
const size = this.currentWorld.size
const horizontalStep = this.contentWidth / size
......@@ -33,11 +34,11 @@ export class WorldView {
// Cells
this.context.lineWidth = 3
this.context.strokeStyle = "white"
this.context.fillStyle = "black"
this.context.strokeStyle = 'white'
this.context.fillStyle = 'black'
this.currentWorld.cells.forEach((cell, coordText) => {
if (cell == Cell.alive) {
let coord = Coord.fromString(coordText)
if (cell === Cell.alive) {
const coord = Coord.fromString(coordText)
const x = coord.x * horizontalStep
const y = coord.y * verticalStep
this.context?.fillRect(x, y, horizontalStep, verticalStep)
......@@ -57,8 +58,8 @@ export class WorldView {
// all stroke() calls are done in just one batch!
// https://www.html5rocks.com/en/tutorials/canvas/performance/
this.context.lineWidth = 0.5
this.context.strokeStyle = "lightgrey"
this.context.strokeStyle = 'lightgrey'
this.context.stroke()
}
}
}
\ No newline at end of file
}
......@@ -2,24 +2,24 @@ import { World } from './World'
import { Coord } from './Coord'
import { WorldView } from './WorldView'
let blinker = World.blinker(new Coord(0, 1))
let beacon = World.beacon(new Coord(10, 10))
let glider = World.glider(new Coord(4, 5))
let block1 = World.block(new Coord(1, 10))
let block2 = World.block(new Coord(18, 3))
let tub = World.tub(new Coord(6, 1))
let alive = blinker.concat(beacon).concat(glider).concat(block1).concat(block2).concat(tub)
const blinker = World.blinker(new Coord(0, 1))
const beacon = World.beacon(new Coord(10, 10))
const glider = World.glider(new Coord(4, 5))
const block1 = World.block(new Coord(1, 10))
const block2 = World.block(new Coord(18, 3))
const tub = World.tub(new Coord(6, 1))
const alive = blinker.concat(beacon).concat(glider).concat(block1).concat(block2).concat(tub)
let canvas = document.getElementById('worldView') as HTMLCanvasElement
let generationLabel = document.getElementById('generationLabel') as HTMLParagraphElement
let worldView = new WorldView(canvas)
const canvas = document.getElementById('worldView') as HTMLCanvasElement
const generationLabel = document.getElementById('generationLabel') as HTMLParagraphElement
const worldView = new WorldView(canvas)
let world = new World(30, alive)
let generation = 0
worldView.world = world
let callback = () => {
const callback = () => {
generation++
world = world.evolve()
worldView.world = world
......
......@@ -14,18 +14,19 @@ const sleep = (ms : number) => {
})
}
let blinker = World.blinker(new Coord(0, 1))
let beacon = World.beacon(new Coord(10, 10))
let glider = World.glider(new Coord(4, 5))
let block1 = World.block(new Coord(1, 10))
let block2 = World.block(new Coord(18, 3))
let tub = World.tub(new Coord(6, 1))
let alive = blinker.concat(beacon).concat(glider).concat(block1).concat(block2).concat(tub)
const blinker = World.blinker(new Coord(0, 1))
const beacon = World.beacon(new Coord(10, 10))
const glider = World.glider(new Coord(4, 5))
const block1 = World.block(new Coord(1, 10))
const block2 = World.block(new Coord(18, 3))
const tub = World.tub(new Coord(6, 1))
const alive = blinker.concat(beacon).concat(glider).concat(block1).concat(block2).concat(tub)
let world = new World(30, alive)
let generation = 0
let callback = async () => {
const callback = async () => {
// eslint-disable-next-line no-unmodified-loop-condition
while (!stop) {
world = world.evolve()
generation++
......
import { World } from "../src/World";
import { Coord } from "../src/Coord";
var tape = require('tape');
tape("Block", (t : any) => {
t.plan(1);
let alive = World.block(new Coord(0, 0));
let original = new World(5, alive);
let next = original.evolve();
t.equal(JSON.stringify(next.cells), JSON.stringify(original.cells));
});
tape("Tub", (t : any) => {
t.plan(1);
let alive = World.tub(new Coord(0, 0));
let original = new World(5, alive);
let next = original.evolve();
t.equal(JSON.stringify(next.cells), JSON.stringify(original.cells));
});
tape("Blinker", (t : any) => {
t.plan(2);
let alive = World.blinker(new Coord(0, 1));
let original = new World(3, alive);
let gen1 = original.evolve();
let expectedAlive: Coord[] = [
new Coord(1, 0),
new Coord(1, 1),
new Coord(1, 2)
];
let expected = new World(3, expectedAlive);
t.equal(JSON.stringify(expected.cells), JSON.stringify(gen1.cells));
let gen2 = gen1.evolve();
t.equal(JSON.stringify(original.cells), JSON.stringify(gen2.cells));
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment