UNPKG

@sbj42/maze-generator-dev

Version:

Development support library for maze-generator plugins

175 lines (151 loc) 6.47 kB
var describe = require('mocha').describe; var it = require('mocha').it; var assert = require('assert'); var Maze = require('@sbj42/maze-generator-core').Maze; var GridMask = require('@sbj42/maze-generator-core').GridMask; function makeRandom(seed) { var randomjs = require('random-js'); var engine = randomjs.MersenneTwister19937.seed(seed); var real = randomjs.real(0, 1); return function() { return real(engine); }; } function isMaze(m) { return m.width && m.height && m.cell; } function cellPassageCount(c) { return (c.north() ? 1 : 0) + (c.east() ? 1 : 0) + (c.south() ? 1 : 0) + (c.west() ? 1 : 0); } function makeMask(width, height) { if (width < 2 || height < 2) throw new Error('cannot test mask with this size: ' + width + 'x' + height); var mask = new GridMask(width, height, { interior: true }); for (var x = 0 ; x < width; x += 2) { for (var y = 0; y < height; y += 2) { mask.set(x, y, false); } } return mask; } function genFunc(algorithmFunc, options) { return function(width, height, seed) { var noptions = {}; for (var k in options) noptions[k] = options[k]; if (seed) noptions.random = makeRandom(seed); else noptions.random = Math.random; if (noptions.mask === true) noptions.mask = makeMask(width, height); var maze = new Maze(width, height); algorithmFunc(maze, noptions); return maze; }; } function testAlgorithmOpt(algorithmFunc, options) { var gen = genFunc(algorithmFunc, options); // Some maze algorithms might intentionally produce no dead ends: // it('should have at least two dead ends', function() { // var m1 = gen(15, 11, 7); // var deadEndCount = 0; // for (var y = 0; y < m1.height(); y ++) { // for (var x = 0; x < m1.width(); x ++) { // if (cellPassageCount(m1.cell(x, y)) == 1) // deadEndCount ++; // } // } // assert.ok(deadEndCount >= 2, 'not enough dead ends'); // }); it('should have passages', function() { var m1 = gen(11, 10, 10); var passages = 0; for (var y = 0; y < m1.height(); y ++) { for (var x = 0; x < m1.width(); x ++) { passages += cellPassageCount(m1.cell(x, y)); } } assert.ok(passages > 0, 'no passages'); }); it('should not have passages that go out of bounds', function() { var m1 = gen(16, 18, 8); for (var y = 0; y < m1.height(); y ++) { assert.ok(!m1.cell(0, y).west(), 'a cell on the west edge should not have a west passage'); assert.ok(!m1.cell(m1.width() - 1, y).east(), 'a cell on the east edge should not have a east passage'); } for (var x = 0; x < m1.width(); x ++) { assert.ok(!m1.cell(x, 0).north(), 'a cell on the north edge should not have a north passage'); assert.ok(!m1.cell(x, m1.height() - 1).south(), 'a cell on the south edge should not have a south passage'); } }); it('should have cells with passages that match', function() { var m1 = gen(16, 18, 9); for (var y = 0; y < m1.height(); y ++) { for (var x = 0; x < m1.width(); x ++) { var cell = m1.cell(x, y); assert.equal(cell.west(), m1.cell(x-1, y).east(), 'west mismatch from ' + x + ',' + y); assert.equal(cell.east(), m1.cell(x+1, y).west(), 'east mismatch from ' + x + ',' + y); assert.equal(cell.north(), m1.cell(x, y-1).south(), 'north mismatch from ' + x + ',' + y); assert.equal(cell.south(), m1.cell(x, y+1).north(), 'south mismatch from ' + x + ',' + y); } } }); if (options.mask) { it('should have masked cells', function() { var m1 = gen(15, 15, 9); var mask = makeMask(m1.width(), m1.height()); for (var y = 0; y < m1.height(); y ++) { for (var x = 0; x < m1.width(); x ++) { if (!mask.get(x, y)) { assert.equal(cellPassageCount(m1.cell(x, y)), 0, 'cell not blocked: ' + x + ',' + y); } } } }); } } function testAlgorithm(algorithmName, algorithmFunc, options) { options = options || {}; describe(algorithmName, function() { var gen = genFunc(algorithmFunc, options); it('can make a very thin maze', function() { assert.ok(isMaze(gen(1, 100))); assert.ok(isMaze(gen(100, 1))); }); it('should return the same maze when seeded the same', function() { var m1 = gen(7, 11, 3); var m2 = gen(7, 11, 3); for (var y = 0; y < m1.height(); y ++) { for (var x = 0; x < m1.width(); x ++) { var c1 = m1.cell(x, y); var c2 = m2.cell(x, y); assert.equal(c1.north(), c2.north(), 'north mismatch at ' + x + ',' + y); assert.equal(c1.east(), c2.east(), 'east mismatch at ' + x + ',' + y); assert.equal(c1.south(), c2.south(), 'south mismatch at ' + x + ',' + y); assert.equal(c1.west(), c2.west(), 'west mismatch at ' + x + ',' + y); } } }); it('should generate no interior cells without passages', function() { var m1 = gen(11, 15, 6); for (var y = 0; y < m1.height(); y ++) { for (var x = 0; x < m1.width(); x ++) { assert.ok(cellPassageCount(m1.cell(x, y)) > 0, 'no passage at ' + x + ',' + y); } } }); testAlgorithmOpt(algorithmFunc, options); }); // algorithm if (algorithmFunc.features && algorithmFunc.features.mask) { describe(algorithmName+'.mask', function() { var noptions = {}; for (var x in options) noptions[x] = options[x]; noptions.mask = true; testAlgorithmOpt(algorithmFunc, noptions); }); } } module.exports = testAlgorithm;