|
|
|
|
|
|
|
|
return cards |
|
|
return cards |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const clip = (v, vmin, vmax) => { |
|
|
|
|
|
return Math.min(Math.max(v, vmin), vmax) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const clipToElement = ([x, y], selector) => { |
|
|
|
|
|
const boundingArea = document.querySelector(selector) |
|
|
|
|
|
const {offsetLeft, offsetTop, offsetWidth, offsetHeight} = boundingArea |
|
|
|
|
|
x = clip(x, offsetLeft, offsetLeft + offsetWidth) |
|
|
|
|
|
y = clip(y, offsetTop, offsetTop + offsetHeight) |
|
|
|
|
|
return [x, y] |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const findNearestDropzoneBFS = (cx, cy) => { |
|
|
|
|
|
try { |
|
|
|
|
|
const dropzone = ['stack', 'card'] |
|
|
|
|
|
const [x, y] = clipToElement([cx, cy], '.stacks') |
|
|
|
|
|
const deque = [JSON.stringify([x, y])] |
|
|
|
|
|
const seen = new Set(deque) |
|
|
|
|
|
while(deque.length && deque.length < 50) { |
|
|
|
|
|
const coord = deque.shift() |
|
|
|
|
|
const [x, y] = JSON.parse(coord) |
|
|
|
|
|
for(const dx of [-1, 0, 1]) { |
|
|
|
|
|
for(const dy of [-1, 0, 1]) { |
|
|
|
|
|
const [nx, ny] = [x + dx, y + dy] |
|
|
|
|
|
const ncoord = JSON.stringify([nx, ny]) |
|
|
|
|
|
if(!seen.has(ncoord)) { |
|
|
|
|
|
deque.push(ncoord) |
|
|
|
|
|
seen.add(ncoord) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
target = document.elementFromPoint(x - scrollX, y - scrollY) |
|
|
|
|
|
isValid = target && dropzone.filter(s => target.classList.contains(s)).length |
|
|
|
|
|
if(isValid) { |
|
|
|
|
|
return target |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
throw 'dropzone not found' |
|
|
|
|
|
} |
|
|
|
|
|
catch(error) { |
|
|
|
|
|
Chat.log(error) |
|
|
|
|
|
const stacks = [...document.querySelectorAll('.stack')] |
|
|
|
|
|
return stacks[Hanabi.nrows * Hanabi.ncols - 1] |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const Counter = { |
|
|
const Counter = { |
|
|
step: (name, delta) => () => { |
|
|
step: (name, delta) => () => { |
|
|
Hanabi.counters[name] += delta |
|
|
Hanabi.counters[name] += delta |
|
|
|
|
|
|
|
|
const stack = stacks[pos] |
|
|
const stack = stacks[pos] |
|
|
stack.sort((a, b) => a.ts - b.ts) |
|
|
stack.sort((a, b) => a.ts - b.ts) |
|
|
const attrs = { |
|
|
const attrs = { |
|
|
|
|
|
pos: pos, |
|
|
ondrop: (ev) => { |
|
|
ondrop: (ev) => { |
|
|
ev.preventDefault() |
|
|
ev.preventDefault() |
|
|
const card = Hanabi.cards.get(+ev.dataTransfer.getData('idx')) |
|
|
const card = Hanabi.cards.get(+ev.dataTransfer.getData('idx')) |
|
|
|
|
|
|
|
|
) |
|
|
) |
|
|
}, |
|
|
}, |
|
|
view: () => m('.hanabi', |
|
|
view: () => m('.hanabi', |
|
|
|
|
|
Hanabi.renderStacks(), |
|
|
m(Counter, {name: 'clues'}), |
|
|
m(Counter, {name: 'clues'}), |
|
|
m(Counter, {name: 'bombs'}), |
|
|
m(Counter, {name: 'bombs'}), |
|
|
Hanabi.renderStacks(), |
|
|
|
|
|
), |
|
|
), |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// requires mobile-drag-drop.js |
|
|
|
|
|
MobileDragDrop.polyfill({ |
|
|
|
|
|
elementFromPoint: findNearestDropzoneBFS, |
|
|
|
|
|
}) |
|
|
|
|
|
addEventListener('touchmove', () => {}) |
|
|
|
|
|
|