summaryrefslogtreecommitdiff
path: root/appl/spree/engines/snap.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/spree/engines/snap.b')
-rw-r--r--appl/spree/engines/snap.b241
1 files changed, 241 insertions, 0 deletions
diff --git a/appl/spree/engines/snap.b b/appl/spree/engines/snap.b
new file mode 100644
index 00000000..ff7166cb
--- /dev/null
+++ b/appl/spree/engines/snap.b
@@ -0,0 +1,241 @@
+implement Engine;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "../spree.m";
+ spree: Spree;
+ Attributes, Range, Object, Clique, Member, rand: import spree;
+include "allow.m";
+ allow: Allow;
+include "cardlib.m";
+ cardlib: Cardlib;
+ publicstack: import cardlib;
+ VERT, HORIZ, TOP, BOTTOM, LEFT, RIGHT, Stackspec: import Cardlib;
+
+clique: ref Clique;
+PLAY, START, SAY, SNAP: con iota;
+
+started := 0;
+
+buttons: ref Object;
+scores: ref Object;
+deck: ref Object;
+
+HAND, PILE: con iota;
+
+hands := array[2] of ref Object;
+piles := array[2] of ref Object;
+
+publicspec: array of Stackspec;
+
+privatespec := array[] of {
+ HAND => Stackspec(Cardlib->sPILE,
+ 52,
+ 0,
+ "hand",
+ HORIZ,
+ BOTTOM),
+ PILE => Stackspec(Cardlib->sPILE,
+ 52,
+ 0,
+ "pile",
+ HORIZ,
+ TOP),
+};
+
+oneplayed := 0; # true if only one member's put down a card so far
+
+MINPLAYERS: con 2;
+MAXPLAYERS: con 2;
+
+clienttype(): string
+{
+ return "cards";
+}
+
+init(g: ref Clique, srvmod: Spree): string
+{
+ sys = load Sys Sys->PATH;
+ clique = g;
+ spree = srvmod;
+
+ allow = load Allow Allow->PATH;
+ if (allow == nil) {
+ sys->print("whist: cannot load %s: %r\n", Allow->PATH);
+ return "bad module";
+ }
+ allow->init(spree, clique);
+ allow->add(SAY, nil, "say &");
+
+ cardlib = load Cardlib Cardlib->PATH;
+ if (cardlib == nil) {
+ sys->print("whist: cannot load %s: %r\n", Cardlib->PATH);
+ return "bad module";
+ }
+
+ cardlib->init(clique, spree);
+ deck = clique.newobject(nil, ~0, "stack");
+ cardlib->makepack(deck, (0, 52), 1);
+ cardlib->shuffle(deck);
+ buttons = clique.newobject(nil, ~0, "buttons");
+ scores = clique.newobject(nil, ~0, "scoretable");
+
+ return nil;
+}
+
+join(p: ref Member): string
+{
+ sys->print("%s(%d) joining\n", p.name(), p.id);
+ if (!started && cardlib->nmembers() < MAXPLAYERS) {
+ (nil, err) := cardlib->join(p, -1);
+ if (err == nil) {
+ if (cardlib->nmembers() == MINPLAYERS) {
+ mkbutton("Start", "start");
+ allow->add(START, nil, "start");
+ }
+ } else
+ sys->print("error on join: %s\n", err);
+ }
+ return nil;
+}
+
+leave(p: ref Member)
+{
+ cardlib->leave(p);
+ started == 0;
+ if (cardlib->nmembers() < MINPLAYERS) {
+ buttons.deletechildren((0, len buttons.children));
+ allow->del(START, nil);
+ }
+}
+
+command(p: ref Member, cmd: string): string
+{
+ e := ref Sys->Exception;
+ if (sys->rescue("parse:*", e) == Sys->EXCEPTION) {
+ sys->rescued(Sys->ONCE, nil);
+ return e.name[6:];
+ }
+ (err, tag, toks) := allow->action(p, cmd);
+ if (err != nil)
+ return err;
+ case tag {
+ START =>
+ buttons.deletechildren((0, len buttons.children));
+ allow->del(START, nil);
+ allow->add(SNAP, nil, "snap");
+ mkbutton("Snap!", "snap");
+ cardlib->startclique(publicspec, privatespec);
+ for (i := 0; i < 2; i++) {
+ hands[i] = cardlib->info(i).stacks[HAND];
+ piles[i] = cardlib->info(i).stacks[PILE];
+ }
+ deck.transfer((0, 26), hands[0], 0);
+ deck.transfer((0, 26), hands[1], 0);
+ canplay(0);
+ canplay(1);
+
+ PLAY =>
+ # click index
+ ord := cardlib->order(p);
+ inf := cardlib->info(ord);
+
+ hand := hands[ord];
+ pile := piles[ord];
+ hand.transfer((len hand.children - 1, len hand.children), pile, len pile.children);
+ cardlib->setface(pile.children[len pile.children - 1], 1);
+ cantplay(ord);
+ oneplayed = !oneplayed;
+ if (!oneplayed || len hands[!ord].children == 0) {
+ for (i := 0; i < 2; i++)
+ if (len hands[i].children > 0)
+ canplay(i);
+ }
+ SNAP =>
+ # snap
+ ord := cardlib->order(p);
+ inf := cardlib->info(ord);
+ if (oneplayed) # XXX allow for case where one person has no cards.
+ return "must wait for two cards to be put down";
+ if (len piles[0].children == 0 || len piles[1].children == 0)
+ return "no cards";
+ c0 := cardlib->getcard(piles[0].children[len piles[0].children - 1]);
+ c1 := cardlib->getcard(piles[1].children[len piles[0].children - 1]);
+ if (c0.number != c1.number) {
+ remark(p.name() + " said snap wrongly!");
+ return "cards must be the same";
+ } else {
+ transferall(piles[!ord], piles[ord], len piles[ord].children);
+ flipstack(piles[ord]);
+ transferall(piles[ord], hands[ord], 0);
+ if (len hands[!ord].children == 0)
+ remark(p.name() + " has won!");
+ oneplayed = 0;
+ for (i := 0; i < 2; i++)
+ if (len hands[i].children > 0)
+ canplay(i);
+ else
+ cantplay(i);
+ }
+ SAY =>
+ clique.action("say member " + string p.id + ": '" + joinwords(tl toks) + "'", nil, nil, ~0);
+ }
+ return nil;
+}
+
+transferall(stack, into: ref Object, idx: int)
+{
+ stack.transfer((0, len stack.children), into, idx);
+}
+
+flipstack(stack: ref Object)
+{
+ for (i := 0; i < len stack.children; i++) {
+ card := stack.children[i];
+ cardlib->setface(card, ! int card.getattr("face"));
+ }
+}
+
+joinwords(v: list of string): string
+{
+ if (v == nil)
+ return nil;
+ s := hd v;
+ for (v = tl v; v != nil; v = tl v)
+ s += " " + hd v;
+ return s;
+}
+
+canplay(ord: int)
+{
+ inf := cardlib->info(ord);
+ allow->del(PLAY, inf.p);
+ allow->add(PLAY, inf.p, "click %d");
+ inf.stacks[HAND].setattr("actions", "click", 1<<inf.p.id);
+}
+
+cantplay(ord: int)
+{
+ inf := cardlib->info(ord);
+ allow->del(PLAY, inf.p);
+ inf.stacks[HAND].setattr("actions", nil, 1<<inf.p.id);
+}
+
+member(ord: int): ref Member
+{
+ return cardlib->info(ord).p;
+}
+
+remark(s: string)
+{
+ clique.action("remark " + s, nil, nil, ~0);
+}
+
+mkbutton(text, cmd: string): ref Object
+{
+ but := clique.newobject(buttons, ~0, "button");
+ but.setattr("text", text, ~0);
+ but.setattr("command", cmd, ~0);
+ return but;
+}