Skip to content

Commit

Permalink
Refactor checkbox constraints to have a unified configuration.
Browse files Browse the repository at this point in the history
  • Loading branch information
sigh committed Jul 30, 2024
1 parent 32cc4c3 commit 4671963
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 350 deletions.
82 changes: 3 additions & 79 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -239,38 +239,9 @@ <h2>Create from selection</h2>
</fieldset>
</form>

<div class="constraint-group">
<div class="constraint-group" id="global-constraints-container">
<h2>Global constraints</h2>

<div>
<input type="checkbox" id="anti-consecutive-input">
<label for="anti-consecutive-input">Anti-Consecutive</label>
<span class="tooltip" data-text="No adjacent cells can have consecutive values.">
</span>
</div>

<div>
<input type="checkbox" id="strict-kropki-input">
<label for="strict-kropki-input">Strict Kropki</label>
<span class="tooltip"
data-text="Only explicitly marked cell pairs satisfy Kropki (black/white dot) constraints.">
</span>
</div>

<div>
<input type="checkbox" id="strict-xv-input">
<label for="strict-xv-input">Strict XV</label>
<span class="tooltip" data-text="Only explicitly marked cell pairs satisfy XV constraints.">
</span>
</div>

<div>
<input type="checkbox" id="global-entropy-input">
<label for="global-entropy-input">Global Entropy</label>
<span class="tooltip"
data-text="Each 2x2 box in the grid has to contain a low digit (1, 2, 3), a middle digit (4, 5, 6) and a high digit (7, 8, 9).">
</span>
</div>
<div id="global-constraint-checkboxes"></div>
</div>

<form id="multi-value-cell-input">
Expand All @@ -295,54 +266,7 @@ <h2>Layout constraints</h2>
<span class="tooltip" data-text="Change the grid shape."></span>
</div>

<div>
<input type="checkbox" id="anti-knight-input">
<label for="anti-knight-input">Anti-Knight</label>
<span class="tooltip" data-text="Cells which are a knight's move away cannot have the same value.">
</span>
</div>

<div>
<input type="checkbox" id="anti-king-input">
<label for="anti-king-input">Anti-King</label>
<span class="tooltip" data-text="Cells which are a king's move away cannot have the same value.">
</span>
</div>

<div>
<input type="checkbox" id="diagonal-plus-input">
<label for="diagonal-plus-input">&#10187; Diagonal</label>
<span class="tooltip" data-text="Values along the positive diagonal must be unique.">
</span>
</div>

<div>
<input type="checkbox" id="diagonal-minus-input">
<label for="diagonal-minus-input">&#10189; Diagonal</label>
<span class="tooltip" data-text="Values along the negative diagonal must be unique.">
</span>
</div>

<div>
<input type="checkbox" id="windoku-input">
<label for="windoku-input">Windoku</label>
<span class="tooltip" data-text="Values in the 3x3 windoku boxes must be uniques.">
</span>
</div>

<div>
<input type="checkbox" id="disjoint-sets">
<label for="disjoint-sets">Disjoint Sets</label>
<span class="tooltip" data-text="No digit may appear in the same position in any two boxes.">
</span>
</div>

<div>
<input type="checkbox" id="no-boxes-input">
<label for="no-boxes-input">No Boxes</label>
<span class="tooltip" data-text="No standard 3x3 box constraints.">
</span>
</div>
<div id="layout-constraint-checkboxes"></div>

<div id="displayed-regions"></div>

Expand Down
147 changes: 147 additions & 0 deletions js/constraint_displays.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class ConstraintDisplays {
static displayOrder() {
return [
this.DefaultRegionsInverted,
this.Windoku,
this.Jigsaw,
this.Indexing,
this.Thermo,
Expand All @@ -13,6 +15,7 @@ class ConstraintDisplays {
this.Dot,
this.Letter,
this.Quad,
this.Diagonal,
]
}
}
Expand All @@ -23,11 +26,14 @@ class BaseConstraintDisplayItem extends DisplayItem {
}

drawItem(constraint, options) { throw 'Unimplemented'; }

removeItem(item) {
item.parentNode.removeChild(item);
return true;
}

toggleItem(constraint, enable) { throw 'Unimplemented'; }

_removeCircleFromPath(p0, p1) {
const [dx, dy] = [p1[0] - p0[0], p1[1] - p0[1]];
const frac = this._CIRCLE_RADIUS / Math.sqrt(dx * dx + dy * dy);
Expand Down Expand Up @@ -677,5 +683,146 @@ ConstraintDisplays.Quad = class Quad extends BaseConstraintDisplayItem {

return quad;
}
}

ConstraintDisplays.Diagonal = class Diagonal extends BaseConstraintDisplayItem {
DIRECTIONS = [1, -1];

constructor(svg) {
super(svg);
this._diagonals = [null, null];

svg.setAttribute('stroke-width', 1);
svg.setAttribute('stroke', 'rgb(255, 0, 0)');
}

_directionIndex(direction) {
return direction > 0;
}

toggleItem(constraint, enable) {
if (enable) {
this._drawDiagonal(constraint.direction);
} else {
this._removeDiagonal(constraint.direction);
}
}

_drawDiagonal(direction) {
const index = this._directionIndex(direction);
if (this._diagonals[index]) return this._diagonals[index];

const shape = this._shape;

const size = DisplayItem.CELL_SIZE * shape.gridSize;
const line = this._makePath([
[0, direction > 0 ? size : 0],
[size, direction > 0 ? 0 : size],
]);

this.getSvg().appendChild(line);
this._diagonals[index] = line;

return line;
}

_removeDiagonal(direction) {
const index = this._directionIndex(direction);
let item = this._diagonals[index];
if (item) item.parentNode.removeChild(item);
this._diagonals[index] = null;
}

clear() {
for (const direction of this.DIRECTIONS) {
this._removeDiagonal(direction);
}
}

reshape(shape) {
super.reshape(shape);

// Redraw the diagonals with the correct shape.
for (const direction of this.DIRECTIONS) {
const index = this._directionIndex(direction);
if (this._diagonals[index]) {
this._removeDiagonal(direction);
this._drawDiagonal(direction);
}
}
}
}

ConstraintDisplays.Windoku = class Windoku extends BaseConstraintDisplayItem {
constructor(svg) {
super(svg);

svg.setAttribute('fill', 'rgb(255, 0, 255)');
svg.setAttribute('opacity', '0.1');

this.clear();
}

clear() {
this.toggleItem(null, false);
}

reshape(shape) {
super.reshape(shape);
super.clear();

const svg = this.getSvg();

for (const region of SudokuConstraint.Windoku.regions(shape)) {
for (const cell of region) {
svg.append(this._makeCellSquare(cell));
}
}
}

toggleItem(_, enable) {
this.getSvg().setAttribute('display', enable ? null : 'none');
}
}

ConstraintDisplays.DefaultRegionsInverted = class DefaultRegionsInverted extends BaseConstraintDisplayItem {
constructor(svg) {
super(svg);

svg.setAttribute('stroke-width', 2);
svg.setAttribute('stroke', 'rgb(0, 0, 0)');
svg.setAttribute('stroke-linecap', 'round');

this.clear();
}

clear() {
this.toggleItem(null, false);
}

reshape(shape) {
super.reshape(shape);
super.clear();

const cellSize = DisplayItem.CELL_SIZE;
const gridSizePixels = cellSize * shape.gridSize;
const svg = this.getSvg();

for (let i = shape.boxWidth; i < shape.gridSize; i += shape.boxWidth) {
svg.appendChild(this._makePath([
[i * cellSize, 0],
[i * cellSize, gridSizePixels],
]));
}
for (let i = shape.boxHeight; i < shape.gridSize; i += shape.boxHeight) {
svg.appendChild(this._makePath([
[0, i * cellSize],
[gridSizePixels, i * cellSize],
]));
}
}

toggleItem(_, enable) {
this.getSvg().setAttribute('display', enable ? 'none' : null);
}
}
Loading

0 comments on commit 4671963

Please sign in to comment.