diff --git a/hanafudapi.cabal b/hanafudapi.cabal index 57b0337..5204ffc 100644 --- a/hanafudapi.cabal +++ b/hanafudapi.cabal @@ -35,7 +35,7 @@ executable hanafudapi build-depends: base >=4.10 && <4.11 , bytestring , containers - , hanafuda >= 0.2.1 + , hanafuda >= 0.3.0 , http-types , aeson , mtl diff --git a/www/fun.js b/www/fun.js index 4ae0b7f..32ac397 100644 --- a/www/fun.js +++ b/www/fun.js @@ -5,7 +5,8 @@ function Fun() { map: map, mapFilter: mapFilter, isSet: isSet, - of: of + of: of, + proj: proj }; function insert(obj, t, compare, min, max) { @@ -44,6 +45,10 @@ function Fun() { return function(key) {return o[key];}; } + function proj(key) { + return function(o) {return o[key];}; + } + function mapFilter(mapper, predicate) { return function(array) { return array.reduce(function(accumulator, elem) { diff --git a/www/game.css b/www/game.css index fea1bed..813ef48 100644 --- a/www/game.css +++ b/www/game.css @@ -2,17 +2,27 @@ clear: both; } -li.card { +#game ul { + padding: 0; +} + +#game .card { + background: #fff; display: inline-block; border-radius: 0.5em; border: 1px solid #555; - width: 6em; - height: 10em; + width: 5em; + height: 8em; + max-height: 10%; float: left; margin: 0.5em; } -#river li.card.candidate, #hand li.card { +#turned.hidden { + display: none; +} + +#river li.card.candidate, #hand.yourTurn li.card { cursor: pointer; } @@ -23,3 +33,20 @@ li.card { #hand li.card.selected { margin-top: -1em; } + +#opponent, #own { + position: absolute; + right: 0; +} + +#opponent { + top: 0; +} + +#own { + bottom: 0; +} + +#opponent .card, #own .card { + margin-left: -2em; +} diff --git a/www/game.js b/www/game.js index 68017e7..69522a0 100644 --- a/www/game.js +++ b/www/game.js @@ -1,26 +1,52 @@ function Game(modules) { + var turned = document.getElementById('turned'); + var status = { + dom: document.getElementById('status'), + playing: false, + step: null, + month: null + }; var sets = { - river : cardSet('river'), - hand : cardSet('hand'), + river: cardSet('river'), + hand: cardSet('hand'), }; var selected = null; - modules.messaging.addEventListener(["NewGame"], function(o) { - o.game.river.forEach(function(cardName) { - var card = new RiverCard(cardName); - sets.river.card[card.value] = card; - sets.river.dom.appendChild(card.dom); - }); - o.game.players[modules.session.getKey()].hand.forEach(function(cardName) { - var card = new HandCard(cardName); - sets.hand.card[card.value] = card; - sets.hand.dom.appendChild(card.dom); - }); + modules.messaging.addEventListener(["Game"], function(o) { + status.step = o.game.step.tag; + setStatus(o.game); + setCaptures(o.game); + [ + {name: 'river', cards: o.game.river}, + {name: 'hand', cards: o.game.players[modules.session.getKey()].hand} + ].forEach(setCardSet); + if(status.step == "Turned") { + turned.textContent = o.game.step.contents; + turned.classList.remove("hidden"); + if(status.playing) { + selected = o.game.step.contents; + showCandidates(modules.hanafuda.Card[selected], true); + } + } else { + turned.classList.add("hidden"); + } + if(status.playing && status.step == "Scored") { + play({koiKoi: confirm( + "You scored ! Do you want to go on ? Press cancel to just get your points and end current month" + )}); + } }); + function play(move) { + modules.messaging.send({ + tag: "Play", + move: move + }); + } + function cardSet(id) { return { - card: {}, + card: null, dom: document.getElementById(id) }; } @@ -29,7 +55,53 @@ function Game(modules) { return modules.fun.mapFilter( modules.fun.of(sets.river.card), modules.fun.isSet - )(modules.hanafuda.sameMonth(card)); + )(modules.hanafuda.sameMonth(card).map(modules.fun.proj('name'))); + } + + function setStatus(game) { + modules.dom.clear(status.dom); + status.month = game.month; + var month = document.createElement('li'); + month.textContent = "This month's flower is the " + status.month; + status.dom.appendChild(month); + var playing = document.createElement('li'); + status.playing = modules.session.is(game.playing); + if(status.playing) { + sets.hand.dom.classList.toggle("yourTurn", status.step == "ToPlay"); + playing.textContent = "Your turn"; + } else { + sets.hand.dom.classList.remove("yourTurn"); + playing.textContent = modules.room.name(game.playing) + " is playing"; + } + status.dom.appendChild(playing); + } + + function setCaptures(game) { + for(var key in game.players) { + var elem = document.getElementById(modules.session.is(key) ? "own" : "opponent"); + elem.getElementsByClassName('score')[0].textContent = game.scores[key] + " pts"; + Array.prototype.forEach.call(elem.getElementsByTagName('ul'), modules.dom.clear); + game.players[key].meld.forEach(function(cardName) { + var card = new Card(cardName); + elem.getElementsByClassName(card.value.family.class)[0].appendChild(card.dom); + }); + } + } + + function setCardSet(conf) { + var set = sets[conf.name]; + var constructor = conf.name == "river" ? RiverCard : HandCard; + modules.dom.clear(set.dom); + set.card = {}; + conf.cards.forEach(function(cardName) { + var card = new constructor(cardName); + set.card[cardName] = card; + set.dom.appendChild(card.dom); + }); + } + + function showCandidates(card, yes) { + matchingInRiver(card).forEach(function(riverCard) {riverCard.setCandidate(yes);}); } function Card(name) { @@ -52,10 +124,11 @@ function Game(modules) { var card = this; return function() { if(card.candidate) { - modules.messaging.send({ - tag: "Play", - move: {capture: [sets.hand.card[selected].name, card.name]} - }); + var withCard = selected; + selected = null; + play( + status.step == 'ToPlay' ? {capture: [withCard, card.name]} : {choose: card.name} + ); } }; }; @@ -70,20 +143,24 @@ function Game(modules) { } HandCard.prototype.onClick = function() { - var card = this; - return function() { - if(selected != undefined) { - sets.hand.card[selected].setSelected(false); - } else { - card.play(); - } - }; + if(status.playing && status.step == "ToPlay") { + var card = this; + return function() { + if(selected != undefined) { + sets.hand.card[selected].setSelected(false); + } else { + card.play(); + } + }; + } else { + return function() {}; + } }; HandCard.prototype.setSelected = function(yes) { - selected = yes ? this.value : null; + selected = yes ? this.name : null; this.dom.classList.toggle('selected', yes); - matchingInRiver(this.value).forEach(function(card) {card.setCandidate(yes);}); + showCandidates(this.value, yes); } HandCard.prototype.play = function() { @@ -91,8 +168,7 @@ function Game(modules) { if(matching.length > 1) { this.setSelected(true); } else { - modules.messaging.send({tag: "Play", move: {play: this.name}}) + play({play: this.name}); } } - } diff --git a/www/hanafuda.js b/www/hanafuda.js index 484250f..1a16cea 100644 --- a/www/hanafuda.js +++ b/www/hanafuda.js @@ -1,4 +1,4 @@ -function Hanafuda() { +function Hanafuda(modules) { var Flower = Object.freeze({ Pine: 0, Plum: 1, @@ -13,7 +13,54 @@ function Hanafuda() { Willow: 10, Paulownia: 11 }); - var Card = Object.freeze({ + + var Family = Object.freeze( + ["Kasu", "Tan", "Tane", "Hikari"].reduce(function(o, name) { + o[name] = {class: name[0].toLowerCase() + name.slice(1)}; + return o; + }, {}) + ); + + var CardNames = [ + "Pine0", "Pine1", "PinePoetry", "Crane", + "Plum0", "Plum1", "PlumPoetry", "BushWarbler", + "Cherry0", "Cherry1", "CherryPoetry", "CampCurtain", + "Wisteria0", "Wisteria1", "WisteriaRed", "Cuckoo", + "Iris0", "Iris1", "IrisRed", "EightPlankBridge", + "Peony0", "Peony1", "PeonyBlue", "Butterflies", + "BushClover0", "BushClover1", "BushCloverRed", "Boar", + "SusukiGrass0", "SusukiGrass1", "Geese", "FullMoon", + "Chrysanthemum0", "Chrysanthemum1", "ChrysanthemumBlue", "SakeCup", + "Maple0", "Maple1", "MapleBlue", "Deer", + "Lightning", "WillowRed", "Swallow", "RainMan", + "Paulownia0", "Paulownia1", "Sand", "Phoenix" + ]; + + var Card = Object.freeze( + CardNames.reduce(function(o, name, i) { + o[name] = { + id: i, + family: findFamily(name, i), + flower: Math.floor(i / 4), + name: name + }; + return o; + }, {}) + ); + + function findFamily(name, i) { + if((i % 4 < 2 && i < 41) || (i > 43 && i < 47)) { + return Family.Kasu; + } else if(name.match(/(Blue|Poetry|Red)/)) { + return Family.Tan; + } else if(["Crane", "CampCurtain", "FullMoon", "RainMan", "Phoenix"].includes(name)) { + return Family.Hikari; + } else { + return Family.Tane; + } + } + + /* Pine0: 0, Pine1: 1, PinePoetry: 2, Crane: 3, Plum0: 4, Plum1: 5, PlumPoetry: 6, BushWarbler: 7, Cherry0: 8, Cherry1: 9, CherryPoetry: 10, CampCurtain: 11, @@ -28,20 +75,49 @@ function Hanafuda() { Paulownia0: 44, Paulownia1: 45, Sand: 46, Phoenix: 47 }); + var kasu = Object.freeze( + Array.apply(null, Array(10)).reduce( + function(t, _, i) {return t.concat(4*i, 4*i + 1);}, [] + ).concat( + ["Lightning", "Paulownia0", "Paulownia1", "Sand"].map(modules.fun.of(Card)) + ).reduce(setKey, {}) + ); + + var tan = Object.freeze( + ["Pine", "Plum", "Cherry"].map(function(name) {return name+"Poetry";}).concat( + ["Wisteria", "Iris", "BushClover"].map(function(name) {return name+"Red";}), + ["Peony", "Chrysanthemum", "Maple"].map(function(name) {return name+"Blue";}) + ) + .map(modules.fun.of(Card)) + .reduce(setKey, {}) + ); + + var tane = Object.freeze( + [ + "BushWarbler", "Cuckoo", "EightPlankBridge", "Butterflies", + "Boar", "Geese", "SakeCup", "Deer", "Swallow" + ] + .map(modules.fun.of(Card)) + .reduce(setKey, {}) + ); + + var light = Object.freeze( + ["Crane", "CampCurtain", "FullMoon", "RainMan", "Phoenix"] + .map(modules.fun.of(Card)) + .reduce(setKey, {}) + ); +*/ + return { Flower: Flower, + Family: Family, Card: Card, - flower: flower, sameMonth: sameMonth }; - function flower(card) { - return Math.floor(card / 4); - } - function sameMonth(card) { - var first = 4 * flower(card); - return [0,1,2,3].map(function(i) {return first + i;}); + var first = 4 * card.flower; + return [0,1,2,3].map(function(i) {return Card[CardNames[first + i]];}); } /* diff --git a/www/index.html b/www/index.html index ffe6aa5..00c9611 100644 --- a/www/index.html +++ b/www/index.html @@ -36,10 +36,26 @@
+

River

+

Hand

+
+ + + + + +
+
+ + + + + +

diff --git a/www/main.js b/www/main.js index 424dd0a..1fc0b86 100644 --- a/www/main.js +++ b/www/main.js @@ -6,8 +6,8 @@ window.addEventListener('load', function() { var session = Session({messaging: messaging}); var room = Room({dom: dom, messaging: messaging, session: session, fun: fun}); var login = Login({dom: dom, messaging: messaging, room: room, screen: screen, session: session}); - var hanafuda = Hanafuda(); - var game = Game({dom: dom, fun: fun, hanafuda: hanafuda, messaging: messaging, session: session}); + var hanafuda = Hanafuda({fun: fun}); + var game = Game({dom: dom, fun: fun, hanafuda: hanafuda, messaging: messaging, room: room, session: session}); messaging.start(); });