|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- const initCards = () => {
- const cards = new Map()
- for(const color of ['red', 'green', 'blue', 'yellow', 'white']) {
- for(const value of [1, 1, 1, 2, 2, 3, 3, 4, 4, 5]) {
- const id = cards.size
- const pos = Hanabi.nrows * Hanabi.ncols - 1
- const ts = Math.random()
- cards.set(id, {id, pos, color, value, ts, faceUp: false})
- }
- }
- return cards
- }
-
- const Counter = {
- step: (name, delta) => () => {
- Hanabi.counters[name] += delta
- Hanabi.sync()
- },
- view: ({attrs}) => m('.counter',
- m('span', attrs.name),
- m('button', {onclick: Counter.step(attrs.name, -1)}, '-'),
- m('span', Hanabi.counters[attrs.name]),
- m('button', {onclick: Counter.step(attrs.name, +1)}, '+'),
- ),
- }
-
- const Hanabi = {
- nrows: 8,
- ncols: 7,
- counters: {clues: 8, bombs: 3},
- players: new Map(),
- cards: [],
- oninit: () => {
- Hanabi.cards = initCards()
- listen('hanabi', ({detail}) => {
- Hanabi.counters = detail.value.counters
- Hanabi.cards = new Map(detail.value.cards)
- })
- listen('join', () => setTimeout(Hanabi.sync, 100))
- doNotLog.add('hanabi')
- },
- sync: () => {
- wire({kind: 'hanabi', value: {
- counters: Hanabi.counters,
- cards: [...Hanabi.cards.entries()],
- }})
- },
- getStacks: () => {
- const totalCount = Hanabi.nrows * Hanabi.ncols
- const stacks = new Array(totalCount).fill(null).map(_ => [])
- Hanabi.cards.forEach((card) => stacks[card.pos].push(card))
- return stacks
- },
- renderCard: (card, i) => {
- const attrs = {
- id: card.id,
- value: card.value,
- style: {top: `${-3 * i}px`},
- ondragstart: (ev) => {
- ev.dataTransfer.setData('idx', card.id)
- ev.dataTransfer.dropEffect = 'move'
- },
- class: ['card', card.color, card.faceUp && 'face-up'].join(' '),
- draggable: true,
- }
- return m('div', attrs)
- },
- renderStack: (pos, stacks) => {
- const stack = stacks[pos]
- stack.sort((a, b) => a.ts - b.ts)
- const attrs = {
- ondrop: (ev) => {
- ev.preventDefault()
- const card = Hanabi.cards.get(+ev.dataTransfer.getData('idx'))
- card.pos = pos
- card.ts = +new Date()
- Hanabi.sync()
- },
- ondragenter: (ev) => {
- /* needed for drag-drop shim */
- ev.preventDefault()
- },
- ondragover: (ev) => {
- ev.preventDefault()
- ev.dataTransfer.dropEffect = 'move'
- },
- }
- const doMany = (fn) => {
- return {onclick: () => {stack.forEach(fn); Hanabi.sync()}}
- }
- const actions = [
- m('button', doMany(card => card.faceUp = !card.faceUp), 'flip'),
- m('button', doMany(card => card.ts = card.id), 'sort'),
- m('button', doMany(card => card.ts = Math.random()), 'shuffle'),
- m('button', doMany(card => card.faceUp = true), 'reveal'),
- ]
- return m('.stack', attrs, stack.map(Hanabi.renderCard),
- stack.length ? m('.actions', actions) : null,
- )
- },
- renderStacks: () => {
- const stacks = Hanabi.getStacks()
- return m('.stacks', {style: {'--ncols': Hanabi.ncols}},
- Array(Hanabi.nrows).fill(null).map((_, y) =>
- Array(Hanabi.ncols).fill(null).map((_, x) =>
- m('.col',
- {owner: State.online[y], class: y < 5 ? State.username === State.online[y] ? 'own' : 'rival' : ''},
- Hanabi.renderStack(y * Hanabi.ncols + x, stacks)),
- )
- )
- )
- },
- view: () => m('.hanabi',
- m(Counter, {name: 'clues'}),
- m(Counter, {name: 'bombs'}),
- Hanabi.renderStacks(),
- ),
- }
|