Sort of reached an acceptable state where two first solver rules work

This commit is contained in:
Tissevert 2022-09-01 21:51:48 +02:00
parent 0a61d9a389
commit 213eec19fe
18 changed files with 213 additions and 279 deletions

View File

@ -1,99 +1,58 @@
import {diagonal, isSmaller, key, plus, ofKey, zero} from Geometry.Vector; import size from Config;
import {at, generate} from Grid.Util; import {isSmaller, plus, zero} from Geometry.Vector;
import * as Vector from Geometry.Vector;
import {at, iter} from Grid.Util;
import * as Matrix from Grid.Util;
import Set;
function CellSet(definition) { var CellSet = Set.make(Vector);
definition = definition || {};
this.cells = {};
if(definition.type == 'rectangle') {
this.fromRectangle(definition.origin, definition.offset);
} else if(definition.type == 'isochrome') {
this.fromIsochrome(definition.grid, definition.origin);
} else if(definition.type == 'union') {
this.fromUnion(definition.sets);
}
}
CellSet.prototype.fromRectangle = function(origin, maxOffset) { return {
generate( CellSet: CellSet,
maxOffset.row, union: CellSet.union,
maxOffset.column, rectangle: rectangle,
function(offset) { isochrome: isochrome,
this.add(plus(origin, offset)); row: row,
}.bind(this) column: column
);
}; };
CellSet.prototype.fromIsochrome = function(grid, origin) { function rectangle(origin, delta) {
var originColor = at(grid, origin); var cellSet = new CellSet();
iter(Matrix.make(delta), function(_, cell) {cellSet.add(plus(origin, cell));});
return cellSet;
}
function isochrome(coloring, origin) {
var cellSet = new CellSet();
var color = at(coloring, origin);
var queue = [origin]; var queue = [origin];
while(queue.length > 0) { while(queue.length > 0) {
var cell = queue.shift(); var cell = queue.shift();
this.add(cell); cellSet.add(cell);
for(var d = -1; d < 2; d += 2) { for(var d = -1; d < 2; d += 2) {
[plus(cell, vertical(d)), plus(cell, horizontal(d))].forEach( [plus(cell, Vector.vertical(d)), plus(cell, Vector.horizontal(d))].forEach(
gateKeeper(this, grid, queue, originColor) gateKeeper(cellSet, coloring, queue, color)
); );
} }
} }
}; return cellSet;
CellSet.prototype.fromUnion = function(sets) {
for(var i = 0; i < sets.length; i++) {
for(var k in sets[i].cells) {
this.cells[k] = true;
}
}
} }
CellSet.prototype.add = function(v) { function gateKeeper(cellSet, coloring, queue, color) {
this.cells[key(v)] = true;
};
CellSet.prototype.remove = function(v) {
delete this.cells[key(v)];
};
CellSet.prototype.contains = function(v) {
return !!this.cells[key(v)];
};
CellSet.prototype.isEmpty = function() {
return Object.keys(this.cells).length < 1;
};
CellSet.prototype.map = function(f) {
var result = [];
for(var k in this.cells) {
result.push(f(ofKey(k)));
}
return result;
}
CellSet.prototype.iter = function(f) {
this.map(f);
}
CellSet.prototype.difference = function(cellSet) {
var newCellSet = new CellSet();
this.iter(function(v) {
if(!cellSet.contains(v)) {
newCellSet.add(v);
}
});
return newCellSet;
}
return {
make: function(definition) {return new CellSet(definition);},
union: function(sets) {return new CellSet({type: 'union', sets: sets});}
};
function gateKeeper(cellSet, grid, queue, originColor) {
return function(cell) { return function(cell) {
if(isSmaller(zero(), cell) && isSmaller(cell, diagonal(grid.length-1)) if(isSmaller(zero(), cell)
&& isSmaller(cell, Vector.diagonal(size-1))
&& !cellSet.contains(cell) && !cellSet.contains(cell)
&& at(grid, cell) == originColor) { && at(coloring, cell) == color) {
queue.push(cell); queue.push(cell);
} }
} }
} }
function row(n) {
return rectangle(Vector.vertical(n), Vector.make(1, size));
}
function column(n) {
return rectangle(Vector.horizontal(n), Vector.make(size, 1));
}

View File

@ -1,5 +1,7 @@
return { return {
make: make, make: make,
getRow: getRow,
getColumn: getColumn,
vertical: vertical, vertical: vertical,
horizontal: horizontal, horizontal: horizontal,
diagonal: diagonal, diagonal: diagonal,
@ -7,12 +9,20 @@ return {
plus: plus, plus: plus,
opposite: opposite, opposite: opposite,
isSmaller: isSmaller, isSmaller: isSmaller,
key: key, toKey: toKey,
ofKey: ofKey ofKey: ofKey,
}; };
function make(row, column) { function make(row, column) {
return {row: row, column: column}; return {row: parseInt(row), column: parseInt(column)};
}
function getRow(v) {
return v.row;
}
function getColumn(v) {
return v.column;
} }
function zero() { function zero() {
@ -32,21 +42,23 @@ function diagonal(length) {
} }
function plus(v0, v1) { function plus(v0, v1) {
return {row: v0.row + v1.row, column: v0.column + v1.column}; return make(v0.row + v1.row, v0.column + v1.column);
} }
function opposite(v) { function opposite(v) {
return {row: -v.row, column: -v.column}; return make(-v.row, -v.column);
} }
function isSmaller(v0, v1) { function isSmaller(v0, v1) {
return v0.row <= v1.row && v0.column <= v1.column; return v0.row <= v1.row && v0.column <= v1.column;
} }
function key(v) { function toKey(v) {
return v.row + ':' + v.column; return v.row + ':' + v.column;
//return 8*v.row + v.column;
} }
function ofKey(k) { function ofKey(k) {
return make.apply(null, k.split(':')); return make.apply(null, k.split(':'));
//return make.call(null, Math.floor(k / 8), k % 8);
} }

View File

@ -1,3 +1,4 @@
import size from Config;
import * as CellSet from Geometry.CellSet; import * as CellSet from Geometry.CellSet;
import * as Dom from UnitJS.Dom; import * as Dom from UnitJS.Dom;
import {at, generate, iter, square} from Grid.Util; import {at, generate, iter, square} from Grid.Util;
@ -8,7 +9,6 @@ var grid = {
cells: null, cells: null,
colors: null, colors: null,
missing: null, missing: null,
size: null
}; };
return { return {
@ -17,9 +17,8 @@ return {
init: init, init: init,
}; };
function init(size, eventHandlers) { function init(eventHandlers) {
grid.size = size; grid.cells = square(size, function(cell) {
grid.cells = generate(size, size, function(cell) {
return Dom.make('td', eventHandlers(cell)); return Dom.make('td', eventHandlers(cell));
}); });
for(var row = 0; row < size; row++) { for(var row = 0; row < size; row++) {
@ -29,13 +28,9 @@ function init(size, eventHandlers) {
} }
function clear() { function clear() {
grid.colors = square(grid.size); grid.colors = square(size);
grid.missing = CellSet.make( grid.missing = CellSet.rectangle(zero(), diagonal(size));
{type: 'rectangle', origin: zero(), offset: diagonal(grid.size)} iter(grid.cells, function(td) {td.className = '';});
);
iter(grid.colors, function(cell) {
at(grid.cells, cell).className = '';
});
} }
function get() { function get() {

View File

@ -12,29 +12,27 @@ return {
function colorize(cell, color) { function colorize(cell, color) {
var grid = Grid.get(); var grid = Grid.get();
set(grid.colors, cell, color || Toolbox.color()); set(grid.colors, cell, color ?? Toolbox.color());
at(Grid.get().cells, cell).className = 'color' + at(grid.colors, cell); at(Grid.get().cells, cell).className = 'color' + at(grid.colors, cell);
grid.missing.remove(cell); grid.missing.remove(cell);
} }
function paint(origin) { function paint(origin) {
var cellSet = CellSet.make( CellSet.isochrome(Grid.get().colors, origin)
{type: 'isochrome', grid: Grid.get().colors, origin: origin} .iter(function(cell) {colorize(cell);});
);
cellSet.iter(colorize);
} }
function setColors(grid) { function setColors(coloring) {
if(grid != undefined) { if(coloring != undefined) {
iter(grid, function(cell) { iter(coloring, function(color, cell) {
if(at(grid, cell) != undefined) { if(color != undefined) {
colorize(cell, at(grid, cell)); colorize(cell, color);
} }
}); });
if(Grid.get().missing.isEmpty()) { if(Grid.get().missing.size() > 0) {
Mode.setEnabled(true, ['play', 'solve']);
} else {
Mode.set('edit'); Mode.set('edit');
} else {
Mode.setEnabled(true, ['play', 'solve']);
} }
} }
} }

View File

@ -1,7 +1,9 @@
import * as Vector from Geometry.Vector; import * as Vector from Geometry.Vector;
return { return {
make: make,
at: at, at: at,
asAFunction: asAFunction,
column: column, column: column,
generate: generate, generate: generate,
iter: iter, iter: iter,
@ -21,18 +23,39 @@ function generate(width, height, f) {
return result; return result;
} }
function make(width, height) {
if(width instanceof Object && width.row != undefined) {
height = width.row;
width = width.column;
}
return Array.from({length: height}).map(
function() {return Array.from({length: width});}
);
}
function iter(grid, f) { function iter(grid, f) {
generate(grid.length > 0 ? grid[0].length : null, grid.length, f); map(grid, f);
}
function map(grid, f) {
return grid.map(function(rowArray, row) {return rowArray.map(
function(value, column) {return f(value, Vector.make(row, column), grid);}
)});
} }
function square(size, value) { function square(size, value) {
return generate(size, size, function() {return value;}); var generator = value instanceof Function ? value : function() {return value;};
return map(make(size, size), function(_, cell) {return generator(cell);});
} }
function at(grid, vector) { function at(grid, vector) {
return grid[vector.row][vector.column]; return grid[vector.row][vector.column];
} }
function asAFunction(grid) {
return function (vector) {return at(grid, vector);};
}
function set(grid, vector, value) { function set(grid, vector, value) {
return grid[vector.row][vector.column] = value; return grid[vector.row][vector.column] = value;
} }

View File

@ -8,17 +8,15 @@ import * as Play from Mode.Play;
import * as Solve from Mode.Solve; import * as Solve from Mode.Solve;
import * as Edit from Mode.Edit; import * as Edit from Mode.Edit;
var size = 8; Toolbox.init();
Toolbox.init(size);
Mode.init({ Mode.init({
play: Play, play: Play,
solve: Solve, solve: Solve,
edit: Edit edit: Edit
}); });
Grid.init(size, Mode.dispatch); Grid.init(Mode.dispatch);
if(window.location.search.length > 0) { if(window.location.search.length > 0) {
var urlSearchParameters = new URLSearchParams(window.location.search); var urlSearchParameters = new URLSearchParams(window.location.search);
Grid.Color.set(Share.decode(size, urlSearchParameters.get('game'))); Grid.Color.set(Share.decode(urlSearchParameters.get('game')));
} }
Grid.IO.init(); Grid.IO.init();

View File

@ -23,7 +23,7 @@ return {
function onEnter() { function onEnter() {
GUI.activate(true, [Grid.get().root, Toolbox.get(), save]); GUI.activate(true, [Grid.get().root, Toolbox.get(), save]);
if(!Grid.get().missing.isEmpty()) { if(Grid.get().missing.size() > 0) {
Mode.setEnabled(false, ['play', 'solve']); Mode.setEnabled(false, ['play', 'solve']);
} else { } else {
Share.link(Grid.get().colors); Share.link(Grid.get().colors);
@ -65,7 +65,7 @@ function colorCell(cell) {
} }
function checkCompleteness() { function checkCompleteness() {
if(Grid.get().missing.isEmpty()) { if(Grid.get().missing.size() < 1) {
Mode.setEnabled(true, ['play', 'solve']); Mode.setEnabled(true, ['play', 'solve']);
Share.link(Grid.get().colors); Share.link(Grid.get().colors);
} }

View File

@ -9,7 +9,7 @@ return {
}; };
function onClick(e, cell) { function onClick(e, cell) {
if(Grid.get().missing.isEmpty()) { if(Grid.get().missing.size() < 1) {
rotateState(at(Grid.get().cells, cell)); rotateState(at(Grid.get().cells, cell));
} }
} }

View File

@ -8,5 +8,5 @@ return {
}; };
function onEnter() { function onEnter() {
console.log(Solver.step(Grid.get().colors)); console.log(Solver.solve(Grid.get().colors));
} }

View File

@ -1,12 +1,14 @@
return { return {
make: make make: make,
Int: make({toKey: id, ofKey: parseInt})
}; };
function id(x) {
return x;
}
function make(type) { function make(type) {
if(type == undefined) { type = type ?? {toKey: id, ofKey: id};
var id = function(x) {return x;};
type = {toKey: id, ofKey: id};
}
function Set(s) { function Set(s) {
this.elements = {}; this.elements = {};
@ -65,5 +67,9 @@ function make(type) {
return newSet; return newSet;
} }
Set.uniq = function(t) {
return (new Set(t)).toList();
}
return Set; return Set;
} }

View File

@ -1,6 +1,8 @@
import size from Config;
import {at, generate, iter, square} from Grid.Util; import {at, generate, iter, square} from Grid.Util;
import * as Decode from Share.Decoder.Protocol; import * as Decode from Share.Decoder.Protocol;
import * as Encode from Share.Encoder.Protocol; import * as Encode from Share.Encoder.Protocol;
import * as Encoder from Share.Encoder.Class;
import GUI; import GUI;
import Grid; import Grid;
@ -16,23 +18,24 @@ function get() {
return share; return share;
} }
function naiveEncode(grid) { function naiveEncode(coloring) {
var encoder = Encoder.make(); var encoder = Encoder.make();
iter(grid, function(cell) { iter(coloring, function(color) {
encoder.int(3)(at(grid, cell)); encoder.int(3)(color);
}); });
return encoder.output(); return encoder.output();
} }
function naiveDecode(size, input) { function naiveDecode(input) {
if(input != undefined) { if(input != undefined) {
var decoder = Decoder.make(input); var decoder = Decoder.make(input);
return generate(size, size, function() {return decoder.int(3);}); return generate(size, size, function() {return decoder.int(3);});
} }
} }
function link(grid) { function link() {
//share.href = '?game=' + naiveEncode(Grid.get().colors); //share.href = '?game=' + naiveEncode(Grid.get().colors);
console.log(naiveEncode(Grid.get().colors));
share.href = '?game=' + Encode.grid(Grid.get().colors); share.href = '?game=' + Encode.grid(Grid.get().colors);
GUI.activate(true, share); GUI.activate(true, share);
} }

View File

@ -1,3 +1,4 @@
import size from Config;
import * as CellSet from Geometry.CellSet; import * as CellSet from Geometry.CellSet;
import * as Decoder from Share.Decoder.Class; import * as Decoder from Share.Decoder.Class;
import {diagonal, plus, zero} from Geometry.Vector; import {diagonal, plus, zero} from Geometry.Vector;
@ -8,12 +9,12 @@ return {
grid: decodeGrid grid: decodeGrid
}; };
function decodeGrid(size, input) { function decodeGrid(input) {
if(input != undefined) { if(input != undefined) {
return decoderLoop( return decoderLoop(
Decoder.make(input), Decoder.make(input),
square(size), square(size),
CellSet.make({type: 'rectangle', origin: zero(), offset: diagonal(size)}) CellSet.rectangle(zero(), diagonal(size))
); );
} }
} }

View File

@ -44,7 +44,7 @@ Encoder.prototype.variableLength6 = function(n) {
Encoder.prototype.int = function(size) { Encoder.prototype.int = function(size) {
return function(n) { return function(n) {
for(var i = 0; i < size; i++) { for(var i = 0; i < size; i++) {
encoder.push(n > 3); this.push(n > 3);
n = (2*n) & 7; n = (2*n) & 7;
} }
}.bind(this); }.bind(this);

View File

@ -1,5 +1,8 @@
import {diagonal, isSmaller, plus} from Geometry.Vector;
import * as Vector from Geometry.Vector;
import * as Encoder from Share.Encoder.Class; import * as Encoder from Share.Encoder.Class;
import {at, iter, square} from Grid.Util; import {at, iter, square} from Grid.Util;
import * as Grid from Grid.Util;
import * as Protocol from Share.Protocol; import * as Protocol from Share.Protocol;
return { return {
@ -10,65 +13,55 @@ function encodeGrid(grid) {
var encoder = Encoder.make(); var encoder = Encoder.make();
var done = square(grid.length, false); var done = square(grid.length, false);
var gradients = square(grid.length); var gradients = square(grid.length);
iter(grid, function(cell) { iter(grid, function(_, cell) {
if(!at(done, cell)) { if(!at(done, cell)) {
let block = getLongestBlock(grid, cell); let block = getLongestBlock(grid, cell);
if(block != undefined) { if(block.size >= Protocol.MIN_BLOCK_SIZE) {
encodeBlock(encoder, block); encodeBlock(encoder, done, block);
} else { } else {
encodeSingleCell( encodeSingleCell(
encoder, encoder,
getColorGradient(gradients, grid, cell) getColorGradient(gradients, grid, done, cell)
); );
} }
} }
}); });
return encoder.output();
} }
function getLongestBlock(grid, origin) { function getLongestBlock(grid, origin) {
var color = at(grid, origin); var color = at(grid, origin);
var longestDirection = findLongestDirection(grid, origin, color); var hSize = getStreak(Grid.row(grid, origin.row), origin.column, color);
var size = extend(grid, origin, color, longestDirection); var vSize = getStreak(Grid.column(grid, origin.column), origin.row, color);
if(size >= Protocol.MIN_BLOCK_SIZE) { return {
return { isVertical: vSize > hSize,
direction: longestDirection.coordinate == 'row' ? 'vertical' : 'horizontal', size: Math.max(hSize, vSize),
size: size, color: color,
color: color origin: origin
}; };
}
} }
function findLongestDirection(grid, p, originColor) { function getStreak(t, firstIndex, color) {
var delta = 1; var fromFirst = t.slice(firstIndex);
while(true) { var index = fromFirst.findIndex(
var newRow = p.row + delta; function(x) {return x != color;}
var newColumn = p.column + delta; );
if(newRow >= grid.length || grid[newRow][p.column] != originColor) { return index >= 0 ? index : fromFirst.length;
return {coordinate: 'column', delta: delta};
} else if(newColumn >= grid[p.row].length || grid[p.row][newColumn] != originColor) {
return {coordinate: 'row', delta: delta+1};
}
delta++;
}
} }
function extend(grid, p, originColor, direction) { function encodeBlock(encoder, done, block) {
var origin = p[direction.coordinate];
p[direction.coordinate] += direction.delta;
while(isSmaller(p, diagonal(grid.length)) && at(grid, p) == originColor) {
p[direction.coordinate]++;
}
return p[direction.coordinate] - origin;
}
function encodeBlock(encoder, block) {
encoder.push(1); encoder.push(1);
encoder.push(block.direction == 'vertical'); encoder.push(block.isVertical);
encoder.variableLength6(block.size - Protocol.MIN_BLOCK_SIZE); encoder.variableLength6(block.size - Protocol.MIN_BLOCK_SIZE);
encoder.int(3)(block.color); encoder.int(3)(block.color);
var cell = Vector.make(block.origin.row, block.origin.column);
for(var i = 0; i < block.size; i++) {
Grid.set(done, cell, true);
cell[block.isVertical ? 'row' : 'column']++;
}
} }
function getColorGradient(gradients, grid, cell) { function getColorGradient(gradients, grid, done, cell) {
if(at(gradients, cell) == undefined) { if(at(gradients, cell) == undefined) {
} }

View File

@ -1,114 +1,54 @@
import * as CellSet from Geometry.CellSet; import * as CellSet from Geometry.CellSet;
import set from Grid.Util;
import * as Inclusion from Solver.Inclusion;
import * as SingleCell from Solver.SingleCell;
import * as State from Solver.State;
import * as Strategy from Solver.Strategy;
return { return {
step: step rate: rate,
solve: solve,
findNextStep: findNextStep
}; };
function step(grid) { function solve(coloring) {
var zones = getZones(grid); var solvingState = State.start(coloring);
var lines = getLines(grid.length); var stuck = false;
var rowClusters = checkRowsInclusions(grid); while(!stuck && solvingState.missing.size() > 0) {
if(rowClusters.length > 0) { Strategy.execute(
rowClusters.forEach(function(rowCluster) { findNextStep(solvingState),
rowCluster.toClear = difference( applyStep(solvingState),
rowCluster.colors.map(function(color) {return zones[color];}), function() {console.log('Solver is stuck'); stuck = true;}
rowCluster.rows.map(function(row) {return lines.rows[row];}) );
);
});
} }
return rowClusters; return solvingState.constellation;
} }
function getZones(grid) { function rate(coloring) {
var zones = {}; }
for(var row = 0; row < grid.length; row++) {
for(var column = 0; column < grid[row].length; column++) { function findNextStep(solvingState) {
var color = grid[row][column]; return Strategy.tryEach([
if(zones[color] == undefined) { Inclusion.find(solvingState),
zones[color] = CellSet.make( SingleCell.find(solvingState)
{type: 'isochrome', row: row, column: column, grid: grid} ]);
); }
function applyStep(solvingState) {
return function(step) {
console.log(step);
['empty', 'star'].forEach(function(attribute) {
if(step[attribute] != undefined) {
step[attribute].iter(function(cell) {
set(solvingState.constellation, cell, attribute == 'star');
forget(solvingState, cell);
});
} }
} });
}
return zones;
}
function line(type, size, i) {
if(type == 'row') {
return CellSet.make(
{type: 'rectangle', row: i, column: 0, width: size, height: 1}
);
} else if(type == 'column') {
return CellSet.make(
{type: 'rectangle', row: 0, column: i, width: 1, height: size}
);
}
}
function getLines(size) {
var empty = Array.from({length: size});
return {
rows: empty.map(function(x, i) {return line('row', size, i);}),
columns: empty.map(function(x, i) {return line('column', size, i);}),
}; };
} }
function getColorsByRow(grid) { function forget(solvingState, cell) {
var colorsByRow = []; State.getCellSets(solvingState).concat(solvingState.missing)
for(var row = 0; row < grid.length; row++) { .forEach(function(cellSet) {cellSet.remove(cell);});
colorsByRow.push(
quotient(grid[row], function(c0, c1) {return c0 == c1;}).map(
function(colorClass) {return colorClass.specimen;}
)
);
}
return colorsByRow;
}
function checkRowsInclusions(grid) {
var colorsByRow = getColorsByRow(grid);
var colorSets = quotient(colorsByRow, sameColorsSet);
return colorSets.reduce(function(commands, colorSet) {
if(colorSet.occurrences.length == colorSet.specimen.length) {
commands.push({
reason: 'rowsInColors',
rows: colorSet.occurrences,
colors: colorSet.specimen
});
}
return commands;
}, []);
}
function quotient(elements, equivalence) {
var classes = [];
elements.forEach(function(element, i) {
for(var c = 0; c < classes.length; c++) {
if(equivalence(element, classes[c].specimen)) {
classes[c].occurrences.push(i);
return;
}
}
classes.push({specimen: element, occurrences: [i]});
});
return classes;
}
function sameColorsSet(s0, s1) {
if(s0.length != s1.length) {
return false;
}
var o0 = {};
s0.forEach(function(x) {o0[x] = true;});
for(var i = 0; i < s1.length; i++) {
if(!o0[s1[i]]) {
return false;
}
}
return true;
}
function difference(setsFrom, setsSubstracted) {
return CellSet.union(setsFrom).difference(CellSet.union(setsSubstracted));
} }

View File

@ -10,7 +10,7 @@ return {
function find(solvingState) { function find(solvingState) {
var cellSets = getCellSets(solvingState); var cellSets = getCellSets(solvingState);
return Strategy.map( return Strategy.map(
stepOfCell, stepOfCell(solvingState.missing),
Strategy.tryEach(cellSets.map(getSingleCellIn)) Strategy.tryEach(cellSets.map(getSingleCellIn))
); );
} }
@ -23,11 +23,13 @@ function getSingleCellIn(cellSet) {
}; };
} }
function stepOfCell(cell) { function stepOfCell(missing) {
return { return function(cell) {
reason: 'singleCell', return {
empty: toEmpty(cell), reason: 'singleCell',
star: new CellSet.CellSet(cell) empty: toEmpty(cell).intersection(missing),
star: new CellSet.CellSet(cell)
};
}; };
} }

View File

@ -1,3 +1,6 @@
import size from Config;
import {asAFunction, iter, square} from Grid.Util;
import {diagonal, zero} from Geometry.Vector;
import * as CellSet from Geometry.CellSet; import * as CellSet from Geometry.CellSet;
return { return {

View File

@ -1,3 +1,4 @@
import size from Config;
import * as Dom from UnitJS.Dom; import * as Dom from UnitJS.Dom;
var toolbox; var toolbox;
@ -11,7 +12,7 @@ return {
tool: tool tool: tool
}; };
function init(size, elementId) { function init(elementId) {
toolbox = document.getElementById(elementId || 'toolbox'); toolbox = document.getElementById(elementId || 'toolbox');
colors = toolbox.querySelector('#colors'); colors = toolbox.querySelector('#colors');
for(var i = 0; i < size; i++) { for(var i = 0; i < size; i++) {