feat: initialize project structure with core application scaffolding and configuration
This commit is contained in:
@@ -0,0 +1,850 @@
|
||||
|
||||
|
||||
|
||||
function randInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function cssRuleExists(match){
|
||||
for(let i = 0; i < document.styleSheets.length; i++){
|
||||
let styleSheet = document.styleSheets[i]
|
||||
let rules = styleSheet.cssRules
|
||||
for(let j = 0; j < rules.length; j++){
|
||||
let rule = rules[j]
|
||||
if(rule.selectorText && rule.selectorText == match){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
class EventHandler {
|
||||
constructor() {
|
||||
this.events = {};
|
||||
this.eventCount = 0;
|
||||
this.suppresEvents = false;
|
||||
this.debugEvents = false;
|
||||
}
|
||||
on(event, listener) {
|
||||
if (!this.events[event]) {
|
||||
this.events[event] = { name:name, listeners: [],callCount: 0};
|
||||
}
|
||||
this.events[event].listeners.push(listener);
|
||||
}
|
||||
off(event, listenerToRemove) {
|
||||
if (!this.events[event]) return;
|
||||
this.events[event].listeners = this.events[event].listeners.filter(listener => listener !== listenerToRemove);
|
||||
}
|
||||
emit(event, data) {
|
||||
if (!this.events[event]) return [];
|
||||
if (this.suppresEvents) return [];
|
||||
this.eventCount++;
|
||||
const returnValue = this.events[event].listeners.map(listener =>{
|
||||
var returnValue = listener(data)
|
||||
if(returnValue == undefined)
|
||||
return null
|
||||
return returnValue
|
||||
});
|
||||
this.events[event].callCount++;
|
||||
if(this.debugEvents){
|
||||
console.debug('debugEvent',{event:event, arg:data, callCount: this.events[event].callCount, number:this.eventCount, returnValues:returnValue})
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
suppres(fn) {
|
||||
const originallySuppressed = this.suppresEvents
|
||||
this.suppresEvents = true
|
||||
fn(this)
|
||||
this.suppresEvents = originallySuppressed
|
||||
return originallySuppressed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Col extends EventHandler {
|
||||
id = 0
|
||||
values = []
|
||||
initial = false
|
||||
_marked = false
|
||||
row = null
|
||||
index = 0
|
||||
valid = true
|
||||
_selected = false
|
||||
_value = 0
|
||||
_isValidXy() {
|
||||
if (!this._value)
|
||||
return true;
|
||||
return this.row.puzzle.fields.filter(field => {
|
||||
return (
|
||||
field.index == this.index && field.value == this._value
|
||||
||
|
||||
field.row.index == this.row.index && field.value == this._value)
|
||||
|
||||
}).filter(field => field != this).length == 0
|
||||
}
|
||||
mark() {
|
||||
this.marked = true
|
||||
}
|
||||
unmark() {
|
||||
this.marked = false
|
||||
}
|
||||
select() {
|
||||
this.selected = true
|
||||
}
|
||||
unselect() {
|
||||
this.selected = false
|
||||
}
|
||||
_isValidBox() {
|
||||
if (!this._value)
|
||||
return true;
|
||||
let startRow = this.row.index - this.row.index % (this.row.puzzle.size / 3);
|
||||
let startCol = this.index - this.index % (this.row.puzzle.size / 3);
|
||||
for (let i = 0; i < this.row.puzzle.size / 3; i++) {
|
||||
for (let j = 0; j < this.row.puzzle.size / 3; j++) {
|
||||
const field = this.row.puzzle.get(i + startRow, j + startCol);
|
||||
if (field != this && field.value == this._value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
validate() {
|
||||
if (!this.row.puzzle.initalized) {
|
||||
return this.valid;
|
||||
}
|
||||
if (this.initial) {
|
||||
this.valid = true
|
||||
return this.valid
|
||||
}
|
||||
if (!this.value && !this.valid) {
|
||||
this.valid = true
|
||||
this.emit('update', this)
|
||||
return this.valid
|
||||
}
|
||||
let oldValue = this.valid
|
||||
this.valid = this._isValidXy() && this._isValidBox();
|
||||
if (oldValue != this.valid) {
|
||||
this.emit('update', this)
|
||||
}
|
||||
return this.valid
|
||||
}
|
||||
set value(val) {
|
||||
if (this.initial)
|
||||
return;
|
||||
const digit = Number(val)
|
||||
const validDigit = digit >= 0 && digit <= 9;
|
||||
let update = validDigit && digit != this.value
|
||||
if (update) {
|
||||
this._value = Number(digit)
|
||||
this.validate()
|
||||
this.emit('update', this);
|
||||
}
|
||||
}
|
||||
get value() {
|
||||
return this._value
|
||||
}
|
||||
get selected() {
|
||||
return this._selected
|
||||
}
|
||||
set selected(val) {
|
||||
if (val != this._selected) {
|
||||
this._selected = val
|
||||
if(this.row.puzzle.initalized)
|
||||
this.emit('update', this);
|
||||
}
|
||||
}
|
||||
get marked() {
|
||||
return this._marked
|
||||
}
|
||||
set marked(val){
|
||||
if(val != this._marked){
|
||||
this._marked = val
|
||||
if(this.row.puzzle.initalized){
|
||||
this.emit('update',this)
|
||||
}
|
||||
}
|
||||
}
|
||||
constructor(row) {
|
||||
super()
|
||||
this.row = row
|
||||
this.index = this.row.cols.length
|
||||
this.id = this.row.puzzle.rows.length * this.row.puzzle.size + this.index;
|
||||
this.initial = false
|
||||
this.selected = false
|
||||
this._value = 0;
|
||||
this.marked = false
|
||||
this.valid = true
|
||||
}
|
||||
update() {
|
||||
this.emit('update',this)
|
||||
}
|
||||
toggleSelected() {
|
||||
this.selected = !this.selected
|
||||
}
|
||||
toggleMarked() {
|
||||
this.marked = !this.marked
|
||||
}
|
||||
get data() {
|
||||
return {
|
||||
values: this.values,
|
||||
value: this.value,
|
||||
index: this.index,
|
||||
id: this.id,
|
||||
row: this.row.index,
|
||||
col: this.index,
|
||||
valid: this.valid,
|
||||
initial: this.initial,
|
||||
selected: this.selected,
|
||||
marked: this.marked
|
||||
}
|
||||
}
|
||||
toString() {
|
||||
return String(this.value)
|
||||
}
|
||||
toText() {
|
||||
return this.toString().replace("0", " ");
|
||||
}
|
||||
}
|
||||
class Row extends EventHandler {
|
||||
cols = []
|
||||
puzzle = null
|
||||
index = 0
|
||||
initialized = false
|
||||
constructor(puzzle) {
|
||||
super()
|
||||
this.puzzle = puzzle
|
||||
this.cols = []
|
||||
this.index = this.puzzle.rows.length
|
||||
const me = this
|
||||
this.initialized = false
|
||||
for (let i = 0; i < puzzle.size; i++) {
|
||||
const col = new Col(this);
|
||||
this.cols.push(col);
|
||||
col.on('update', (field) => {
|
||||
me.emit('update', field)
|
||||
})
|
||||
}
|
||||
this.initialized = true
|
||||
}
|
||||
get data() {
|
||||
return {
|
||||
cols: this.cols.map(col => col.data),
|
||||
index: this.index
|
||||
}
|
||||
}
|
||||
toText() {
|
||||
let result = ''
|
||||
for (let col of this.cols) {
|
||||
result += col.toText();
|
||||
}
|
||||
return result
|
||||
}
|
||||
toString() {
|
||||
return this.toText().replaceAll(" ", "0");
|
||||
}
|
||||
}
|
||||
|
||||
class Puzzle extends EventHandler {
|
||||
rows = []
|
||||
size = 0
|
||||
hash = 0
|
||||
states = []
|
||||
parsing = false
|
||||
_initialized = false
|
||||
initalized = false
|
||||
_fields = null
|
||||
constructor(arg) {
|
||||
super()
|
||||
this.debugEvents = true;
|
||||
this.initalized = false
|
||||
this.rows = []
|
||||
if (isNaN(arg)) {
|
||||
// load session
|
||||
} else {
|
||||
this.size = Number(arg)
|
||||
}
|
||||
for (let i = 0; i < this.size; i++) {
|
||||
const row = new Row(this);
|
||||
this.rows.push(row);
|
||||
row.on('update', (field) => {
|
||||
this.onFieldUpdate(field)
|
||||
})
|
||||
}
|
||||
this._initialized = true
|
||||
this.initalized = true
|
||||
this.commitState()
|
||||
}
|
||||
validate() {
|
||||
return this.valid;
|
||||
}
|
||||
_onEventHandler(){
|
||||
this.eventCount++;
|
||||
}
|
||||
makeInvalid() {
|
||||
if (!app.valid) {
|
||||
let invalid = this.invalid;
|
||||
return invalid[invalid.length - 1];
|
||||
}
|
||||
this.rows.forEach(row => {
|
||||
row.cols.forEach(col => {
|
||||
if (col.value) {
|
||||
let modify = null;
|
||||
if (col.index == this.size) {
|
||||
modify = this.get(row.index, col.index - 2);
|
||||
} else {
|
||||
modify = this.get(row.index, col.index + 1);
|
||||
}
|
||||
modify.value = col.value
|
||||
// last one is invalid
|
||||
return modify.index > col.index ? modify : col;
|
||||
}
|
||||
col.valid = false
|
||||
})
|
||||
})
|
||||
this.get(0, 0).value = 1;
|
||||
this.get(0, 1).value = 1;
|
||||
return this.get(0, 1);
|
||||
}
|
||||
reset() {
|
||||
this._initialized = false
|
||||
this.initalized == false;
|
||||
this.parsing = true
|
||||
this.fields.forEach(field => {
|
||||
field.initial = false
|
||||
field.selected = false
|
||||
field.marked = false
|
||||
field.value = 0
|
||||
})
|
||||
this.hash = 0
|
||||
this.states = []
|
||||
this.parsing = false
|
||||
this.initalized = true
|
||||
this._initialized = true
|
||||
this.commitState()
|
||||
}
|
||||
get valid() {
|
||||
return this.invalid.length == 0
|
||||
}
|
||||
get invalid() {
|
||||
this.emit('validating',this)
|
||||
const result = this.fields.filter(field => !field.validate())
|
||||
this.emit('validated',this)
|
||||
return result
|
||||
}
|
||||
get selected() {
|
||||
return this.fields.filter(field => field.selected)
|
||||
}
|
||||
get marked(){
|
||||
return this.fields.filter(field=>field.marked)
|
||||
}
|
||||
loadString(content) {
|
||||
this.emit('parsing', this)
|
||||
this.reset()
|
||||
this.parsing = true
|
||||
this.initalized = false;
|
||||
this._initialized = false;
|
||||
|
||||
const regex = /\d/g;
|
||||
const matches = [...content.matchAll(regex)]
|
||||
let index = 0;
|
||||
const max = this.size * this.size;
|
||||
matches.forEach(match => {
|
||||
const digit = Number(match[0]);
|
||||
let field = this.fields[index]
|
||||
field.value = digit;
|
||||
field.initial = digit != 0
|
||||
index++;
|
||||
});
|
||||
this._initialized = true;
|
||||
this.parsing = false
|
||||
this.deselect();
|
||||
this.initalized = true;
|
||||
this.suppres(()=>{
|
||||
this.fields.forEach((field)=>{
|
||||
field.update()
|
||||
})
|
||||
})
|
||||
this.commitState()
|
||||
this.emit('parsed', this)
|
||||
this.emit('update',this)
|
||||
}
|
||||
get state() {
|
||||
return this.getData(true)
|
||||
}
|
||||
get previousState() {
|
||||
if (this.states.length == 0)
|
||||
return null;
|
||||
return this.states.at(this.states.length - 1)
|
||||
}
|
||||
get stateChanged() {
|
||||
if (!this._initialized)
|
||||
return false
|
||||
return !this.previousState || this.state != this.previousState
|
||||
}
|
||||
|
||||
commitState() {
|
||||
if (!this.initalized)
|
||||
return false;
|
||||
this.hash = this._generateHash()
|
||||
if (this.stateChanged) {
|
||||
this.states.push(this.state)
|
||||
this.emit('commitState', this)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
onFieldUpdate(field) {
|
||||
if (!this.initalized)
|
||||
return false;
|
||||
if (!this._initialized)
|
||||
return;
|
||||
this.validate();
|
||||
this.commitState();
|
||||
this.emit('update', this)
|
||||
}
|
||||
|
||||
get data() {
|
||||
return this.getData(true)
|
||||
}
|
||||
|
||||
popState() {
|
||||
let prevState = this.previousState
|
||||
if (!prevState)
|
||||
{
|
||||
this.deselect()
|
||||
return null
|
||||
}while (prevState && prevState.hash == this.state.hash)
|
||||
prevState = this.states.pop()
|
||||
if (!prevState)
|
||||
{
|
||||
this.deselect()
|
||||
return null
|
||||
}
|
||||
this.applyState(prevState)
|
||||
this.emit('popState', this)
|
||||
return prevState
|
||||
}
|
||||
applyState(newState) {
|
||||
|
||||
this._initialized = false
|
||||
newState.fields.forEach(stateField => {
|
||||
let field = this.get(stateField.row, stateField.col)
|
||||
field.selected = stateField.selected
|
||||
field.values = stateField.values
|
||||
field.value = stateField.value
|
||||
field.initial = stateField.initial
|
||||
field.validate()
|
||||
})
|
||||
this._initialized = true
|
||||
this.emit('stateApplied', this)
|
||||
this.emit('update', this)
|
||||
}
|
||||
getData(withHash = false) {
|
||||
let result = {
|
||||
fields: this.fields.map(field => field.data),
|
||||
size: this.size,
|
||||
valid: this.valid
|
||||
}
|
||||
if (withHash) {
|
||||
result['hash'] = this._generateHash()
|
||||
}
|
||||
return result;
|
||||
}
|
||||
get(row, col) {
|
||||
if (!this.initalized)
|
||||
return null;
|
||||
if (!this.rows.length)
|
||||
return null;
|
||||
if (!this.rows[row])
|
||||
return null;
|
||||
return this.rows[row].cols[col];
|
||||
}
|
||||
get fields() {
|
||||
if (this._fields == null) {
|
||||
this._fields = []
|
||||
for (let row of this.rows) {
|
||||
for (let col of row.cols) {
|
||||
this._fields.push(col)
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._fields
|
||||
}
|
||||
_generateHash() {
|
||||
var result = 0;
|
||||
JSON.stringify(this.getData(false)).split('').map(char => {
|
||||
return char.charCodeAt(0) - '0'.charCodeAt(0)
|
||||
}).forEach(num => {
|
||||
result += 26
|
||||
result = result + num
|
||||
})
|
||||
return result
|
||||
}
|
||||
get text() {
|
||||
let result = ''
|
||||
for (let row of this.rows) {
|
||||
result += row.toText() + "\n"
|
||||
}
|
||||
result = result.slice(0, result.length - 1)
|
||||
return result
|
||||
}
|
||||
get initialFields() {
|
||||
return this.fields.filter(field => field.initial)
|
||||
}
|
||||
get json() {
|
||||
return JSON.stringify(this.data)
|
||||
}
|
||||
get zeroedText() {
|
||||
return this.text.replaceAll(" ", "0")
|
||||
}
|
||||
get string() {
|
||||
return this.toString()
|
||||
}
|
||||
toString() {
|
||||
return this.text.replaceAll("\n","").replaceAll(" ", "0")
|
||||
}
|
||||
get humanFormat() {
|
||||
return ' ' + this.text.replaceAll(" ", "0").split("").join(" ")
|
||||
}
|
||||
getRandomField() {
|
||||
const emptyFields = this.empty;
|
||||
return emptyFields[randInt(0, emptyFields.length - 1)]
|
||||
}
|
||||
update(callback) {
|
||||
this.commitState()
|
||||
this.intalized = false
|
||||
callback(this);
|
||||
this.intalized = true
|
||||
this.validate()
|
||||
this.commitState()
|
||||
this.intalized = false
|
||||
if(this.valid)
|
||||
this.deselect()
|
||||
this.intalized = true
|
||||
this.emit('update', this)
|
||||
}
|
||||
get empty() {
|
||||
return this.fields.filter(field => field.value == 0)
|
||||
}
|
||||
getRandomEmptyField() {
|
||||
let field = this.getRandomField()
|
||||
if (!field)
|
||||
return null
|
||||
return field
|
||||
}
|
||||
deselect() {
|
||||
this.fields.forEach(field => field.selected = false)
|
||||
}
|
||||
generate() {
|
||||
this.reset()
|
||||
this.initalized = false
|
||||
for (let i = 0; i < 17; i++) {
|
||||
this.fillRandomField()
|
||||
}
|
||||
this.deselect()
|
||||
this.initalized = true
|
||||
this.commitState()
|
||||
this.emit('update',this)
|
||||
}
|
||||
fillRandomField() {
|
||||
let field = this.getRandomEmptyField()
|
||||
if (!field)
|
||||
return
|
||||
this.deselect()
|
||||
field.selected = true
|
||||
let number = 0
|
||||
number++;
|
||||
|
||||
while (number <= 9) {
|
||||
field.value = randInt(1, 9)
|
||||
field.update()
|
||||
if (this.validate()) {
|
||||
field.initial = true
|
||||
return field
|
||||
}
|
||||
number++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PuzzleManager {
|
||||
constructor(size) {
|
||||
this.size = size
|
||||
this.puzzles = []
|
||||
this._activePuzzle = null
|
||||
}
|
||||
|
||||
addPuzzle(puzzle){
|
||||
this.puzzles.push(puzzle)
|
||||
}
|
||||
get active(){
|
||||
return this.activePuzzle
|
||||
}
|
||||
set activePuzzle(puzzle){
|
||||
this._activePuzzle = puzzle
|
||||
}
|
||||
get activePuzzle(){
|
||||
return this._activePuzzle
|
||||
}
|
||||
}
|
||||
|
||||
const puzzleManager = new PuzzleManager(9)
|
||||
|
||||
class Sudoku extends HTMLElement {
|
||||
styleSheet = `
|
||||
.sudoku {
|
||||
font-size: 13px;
|
||||
color:#222;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(9, 1fr);
|
||||
grid-template-rows: auto;
|
||||
gap: 0px;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
background-color: #e5e5e5;
|
||||
border-radius: 5px;
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
.sudoku-field-initial {
|
||||
color: #777;
|
||||
}
|
||||
.sudoku-field-selected {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
.soduku-field-marked {
|
||||
background-color: blue;
|
||||
}
|
||||
.sudoku-field-invalid {
|
||||
color: red;
|
||||
}
|
||||
.sudoku-field {
|
||||
border: 1px solid #ccc;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
aspect-ratio: 1/1;
|
||||
}`
|
||||
set fieldSize(val) {
|
||||
this._fieldSize = val ? Number(val) : null
|
||||
this.fieldElements.forEach(field => {
|
||||
field.style.fontSize = this._fieldSize ? this._fieldSize.toString() +'px' : ''
|
||||
})
|
||||
}
|
||||
get fieldSize(){
|
||||
return this._fieldSize
|
||||
}
|
||||
get eventCount() {
|
||||
return this.puzzle.eventCount
|
||||
}
|
||||
get puzzleContent(){
|
||||
return this.puzzle.humanFormat
|
||||
}
|
||||
set puzzleContent(val) {
|
||||
if (val == "generate") {
|
||||
this.puzzle.generate()
|
||||
} else if (val) {
|
||||
this.puzzle.loadString(val)
|
||||
} else {
|
||||
this.puzzle.reset()
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
this.puzzleContent = this.getAttribute('puzzle') ? this.getAttribute('puzzle') : null
|
||||
this._fieldSize = null
|
||||
this.fieldSize = this.getAttribute('size') ? this.getAttribute('size') : null
|
||||
this.readOnly = this.getAttribute('read-only') ? true : false
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this.shadowRoot.appendChild(this.styleElement)
|
||||
this.shadowRoot.appendChild(this.puzzleDiv)
|
||||
}
|
||||
toString(){
|
||||
return this.puzzleContent
|
||||
}
|
||||
set active(val){
|
||||
this._active = val
|
||||
if(this._active)
|
||||
this.manager.activePuzzle =this
|
||||
}
|
||||
get active(){
|
||||
return this._active
|
||||
}
|
||||
set readOnly(val){
|
||||
this._readOnly = val ? true : false
|
||||
}
|
||||
get readOnly(){
|
||||
return this._readOnly
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
this._readOnly = false;
|
||||
this._active = false
|
||||
this.fieldElements = []
|
||||
this.puzzle = new Puzzle(9)
|
||||
this.fields = []
|
||||
this.styleElement = document.createElement('style');
|
||||
this.styleElement.textContent = this.styleSheet
|
||||
this.puzzleDiv = document.createElement('div')
|
||||
this.puzzleDiv.classList.add('sudoku');
|
||||
this._bind()
|
||||
this.manager.addPuzzle(this)
|
||||
}
|
||||
get manager() {
|
||||
return puzzleManager
|
||||
}
|
||||
_bind(){
|
||||
this._bindFields()
|
||||
this._bindEvents()
|
||||
this._sync()
|
||||
}
|
||||
_bindFields(){
|
||||
const me = this
|
||||
this.puzzle.rows.forEach((row) => {
|
||||
row.cols.forEach((field) => {
|
||||
const fieldElement = document.createElement('div');
|
||||
fieldElement.classList.add('sudoku-field');
|
||||
fieldElement.field = field
|
||||
field.on('update', (field) => {
|
||||
me._sync()
|
||||
})
|
||||
fieldElement.addEventListener('click', (e) => {
|
||||
if(!me.readOnly)
|
||||
field.toggleSelected()
|
||||
})
|
||||
fieldElement.addEventListener('contextmenu',(e)=>{
|
||||
e.preventDefault()
|
||||
field.row.puzzle.update(()=>{
|
||||
field.selected = false
|
||||
field.value = 0
|
||||
})
|
||||
})
|
||||
this.fields.push(field)
|
||||
this.fieldElements.push(fieldElement)
|
||||
this.puzzleDiv.appendChild(fieldElement);
|
||||
});
|
||||
});
|
||||
}
|
||||
_bindEvents(){
|
||||
const me = this
|
||||
this.puzzle.on('update', () => {
|
||||
me._sync()
|
||||
});
|
||||
this.puzzleDiv.addEventListener('mouseenter', (e) => {
|
||||
me.active = true
|
||||
})
|
||||
this.puzzleDiv.addEventListener('mouseexit', (e) => {
|
||||
me.active = false
|
||||
})
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if(me.readOnly)
|
||||
return
|
||||
if (!puzzleManager.active)
|
||||
return
|
||||
const puzzle = puzzleManager.active.puzzle
|
||||
if (e.key == 'u') {
|
||||
puzzle.popState();
|
||||
} else if (e.key == 'd') {
|
||||
puzzle.update((target) => {
|
||||
puzzle.selected.forEach(field => {
|
||||
field.value = 0
|
||||
});
|
||||
})
|
||||
} else if (e.key == 'a') {
|
||||
puzzle.autoSolve()
|
||||
} else if (e.key == 'r') {
|
||||
puzzle.fillRandomField();
|
||||
} else if (!isNaN(e.key)) {
|
||||
puzzle.update((target) => {
|
||||
puzzle.selected.forEach(field => {
|
||||
field.value = Number(e.key)
|
||||
})
|
||||
});
|
||||
} else if(e.key == 'm'){
|
||||
let fields = [];
|
||||
puzzle.update((target) => {
|
||||
target.selected.forEach(field => {
|
||||
field.selected = false;
|
||||
fields.push(field)
|
||||
});
|
||||
});
|
||||
puzzle.update((target)=>{
|
||||
fields.forEach((field)=>{
|
||||
field.toggleMarked();
|
||||
})
|
||||
});
|
||||
puzzle.emit('update',puzzle);
|
||||
}
|
||||
})
|
||||
}
|
||||
autoSolve() {
|
||||
const me = this
|
||||
window.requestAnimationFrame(() => {
|
||||
if (me.fillRandomField()) {
|
||||
if (me.empty.length)
|
||||
return me.autoSolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
get(row, col){
|
||||
return this.puzzle.get(row,col)
|
||||
}
|
||||
_syncField(fieldElement) {
|
||||
const field = fieldElement.field
|
||||
fieldElement.classList.remove('sudoku-field-selected')
|
||||
fieldElement.classList.remove('sudoku-field-empty')
|
||||
fieldElement.classList.remove('sudoku-field-invalid')
|
||||
fieldElement.classList.remove('sudoku-field-initial')
|
||||
fieldElement.classList.remove('sudoku-field-marked')
|
||||
console.info('Removed marked class');
|
||||
fieldElement.innerHTML = field.value ? field.value.toString() : ' '
|
||||
|
||||
if (field.selected) {
|
||||
fieldElement.classList.add('sudoku-field-selected')
|
||||
window.selected = field.field
|
||||
}
|
||||
if (!field.valid) {
|
||||
fieldElement.classList.add('sudoku-field-invalid')
|
||||
}
|
||||
if (!field.value) {
|
||||
fieldElement.classList.add('sudoku-field-empty')
|
||||
}
|
||||
if(field.initial){
|
||||
fieldElement.classList.add('sudoku-field-initial')
|
||||
}
|
||||
if(field.marked){
|
||||
fieldElement.classList.add('sudoku-field-marked')
|
||||
console.info("added marked lcass")
|
||||
}
|
||||
|
||||
}
|
||||
_sync() {
|
||||
this.fieldElements.forEach(fieldElement => {
|
||||
this._syncField(fieldElement);
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
customElements.define("my-sudoku", Sudoku);
|
||||
|
||||
function generateIdByPosition(element) {
|
||||
// Get the parent element
|
||||
const parent = element.parentNode;
|
||||
|
||||
// Get the index of the element within its parent
|
||||
const index = Array.prototype.indexOf.call(parent.children, element);
|
||||
|
||||
// Generate a unique ID using the tag name and index
|
||||
const generatedId = `${element.tagName.toLowerCase()}-${index}`;
|
||||
|
||||
// Assign the generated ID to the element
|
||||
element.id = generatedId.replace('div-', 'session-key-');
|
||||
|
||||
return element.id;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user