|  |  | @@ -0,0 +1,114 @@ | 
		
	
		
			
			|  |  |  | 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() | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | 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(), | 
		
	
		
			
			|  |  |  | ), | 
		
	
		
			
			|  |  |  | } |