summaryrefslogtreecommitdiff
path: root/appl/spree/engines/canfield.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/spree/engines/canfield.b')
-rw-r--r--appl/spree/engines/canfield.b340
1 files changed, 340 insertions, 0 deletions
diff --git a/appl/spree/engines/canfield.b b/appl/spree/engines/canfield.b
new file mode 100644
index 00000000..dbf3734f
--- /dev/null
+++ b/appl/spree/engines/canfield.b
@@ -0,0 +1,340 @@
+implement Gatherengine;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sets.m";
+ All, None: import Sets;
+include "../spree.m";
+ spree: Spree;
+ Attributes, Range, Object, Clique, Member: import spree;
+include "allow.m";
+ allow: Allow;
+include "cardlib.m";
+ cardlib: Cardlib;
+ Selection, Cmember, Card: import cardlib;
+ dTOP, dRIGHT, dLEFT, oDOWN,
+ aCENTRELEFT, aUPPERRIGHT,
+ EXPAND, FILLX, FILLY, Stackspec: import Cardlib;
+include "../gather.m";
+
+clique: ref Clique;
+
+sevens: array of ref Object; # [7]
+spare1, spare2: ref Object;
+acepiles: array of ref Object; # [4]
+top2botcount := 3;
+top2bot: ref Object;
+
+CLICK, TOP2BOT, REDEAL, SHOW: con iota;
+
+Openspec := Stackspec(
+ "display", # style
+ 19, # maxcards
+ 0, # conceal
+ "" # title
+);
+
+Pilespec := Stackspec(
+ "pile", # style
+ 19, # maxcards
+ 0, # conceal
+ "pile" # title
+);
+
+Untitledpilespec := Stackspec(
+ "pile", # style
+ 13, # maxcards
+ 0, # conceal
+ "" # title
+);
+
+clienttype(): string
+{
+ return "cards";
+}
+
+rank := array[] of {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
+
+maxmembers(): int
+{
+ return 1;
+}
+
+init(srvmod: Spree, g: ref Clique, nil: list of string, nil: int): 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);
+ cardlib = load Cardlib Cardlib->PATH;
+ if (cardlib == nil) {
+ sys->print("whist: cannot load %s: %r\n", Cardlib->PATH);
+ return "bad module";
+ }
+ cardlib->init(spree, clique);
+ return nil;
+}
+
+propose(members: array of string): string
+{
+ if (len members != 1)
+ return "one member only";
+ return nil;
+}
+
+start(members: array of ref Member, archived: int)
+{
+ allow->add(SHOW, nil, "show");
+ if (archived) {
+ archiveobj := cardlib->unarchive();
+ sevens = cardlib->getarchivearray("sevens");
+ acepiles = cardlib->getarchivearray("acepiles");
+ spare1 = cardlib->getarchiveobj("spare1");
+ spare2 = cardlib->getarchiveobj("spare2");
+ top2bot = cardlib->getarchiveobj("top2bot");
+ top2botcount = int archiveobj.getattr("top2botcount");
+
+ allow->unarchive(archiveobj);
+ archiveobj.delete();
+ } else {
+ p := members[0];
+ Cmember.join(p, -1).layout.lay.setvisibility(All);
+ startclique();
+ allow->add(CLICK, p, "click %o %d");
+ allow->add(TOP2BOT, p, "top2bot");
+ allow->add(REDEAL, p, "redeal");
+ }
+}
+
+archive()
+{
+ archiveobj := cardlib->archive();
+ cardlib->archivearray(sevens, "sevens");
+ cardlib->archivearray(acepiles, "acepiles");
+ cardlib->setarchivename(spare1, "spare1");
+ cardlib->setarchivename(spare2, "spare2");
+ cardlib->setarchivename(top2bot, "top2bot");
+ archiveobj.setattr("top2botcount", string top2botcount, None);
+ allow->archive(archiveobj);
+}
+
+command(p: ref Member, cmd: string): string
+{
+ (err, tag, toks) := allow->action(p, cmd);
+ if (err != nil)
+ return err;
+ cp := Cmember.find(p);
+ if (cp == nil)
+ return "you are not playing";
+
+ case tag {
+ CLICK =>
+ # click stack index
+ stack := clique.objects[int hd tl toks];
+ nc := len stack.children;
+ idx := int hd tl tl toks;
+ sel := cp.sel;
+ stype := stack.getattr("type");
+ if (sel.isempty() || sel.stack == stack) {
+ if (nc == 0 && stype == "spare1") {
+ cardlib->flip(spare2);
+ spare2.transfer((0, len spare2.children), spare1, 0);
+ return nil;
+ }
+ if (idx < 0 || idx >= len stack.children)
+ return "invalid index";
+ case stype {
+ "spare2" or
+ "open" =>
+ select(cp, stack, (idx, nc));
+ "spare1" =>
+ if ((n := nc) > 3)
+ n = 3;
+ for (i := 0; i < n; i++) {
+ cardlib->setface(stack.children[nc - 1], 1);
+ stack.transfer((nc - 1, nc), spare2, -1);
+ nc--;
+ }
+ * =>
+ return "you can't move cards from there";
+ }
+ } else {
+ from := sel.stack;
+ case stype {
+ "acepile" =>
+ if (sel.r.end != sel.r.start + 1)
+ return "only one card at a time!";
+ card := getcard(sel.stack.children[sel.r.start]);
+ if (nc == 0) {
+ if (card.number != 0)
+ return "aces only";
+ } else {
+ top := getcard(stack.children[nc - 1]);
+ if (card.number != top.number + 1)
+ return "out of sequence";
+ if (card.suit != top.suit)
+ return "wrong suit";
+ }
+ sel.transfer(stack, -1);
+ "open" =>
+ c := getcard(sel.stack.children[sel.r.start]);
+ col := !isred(c);
+ n := c.number + 1;
+ for (i := sel.r.start; i < sel.r.end; i++) {
+ c2 := getcard(sel.stack.children[i]);
+ if (c2.face == 0)
+ return "cannot move face-down cards";
+ if (isred(c2) == col)
+ return "bad colour sequence";
+ if (c2.number != n - 1)
+ return "bad number sequence";
+ n = c2.number;
+ col = isred(c2);
+ }
+ if (nc != 0) {
+ c2 := getcard(stack.children[nc - 1]);
+ if (isred(c2) == isred(c) || c2.number != c.number + 1)
+ return "invalid move";
+ } else if (c.number != 12)
+ return "only kings allowed there";
+ sel.transfer(stack, -1);
+ * =>
+ return "can't move there";
+ }
+ if (from.getattr("type") == "open" && len from.children > 0)
+ cardlib->setface(from.children[len from.children - 1], 1);
+ }
+ TOP2BOT =>
+ if (len spare2.children != 0)
+ return "can only top-to-bottom on the whole pile";
+ if (top2botcount <= 0)
+ return "too late";
+ nc := len spare1.children;
+ if (nc > 0) {
+ spare1.transfer((nc - 1, nc), spare1, 0);
+ top2botcount--;
+ settop2bottext();
+ }
+ REDEAL =>
+ clearup();
+ cardlib->shuffle(spare1);
+ deal();
+ top2botcount = 3;
+ settop2bottext();
+ SHOW =>
+ clique.show(nil);
+ }
+ return nil;
+}
+
+readfile(nil: int, nil: big, nil: int): array of byte
+{
+ return nil;
+}
+
+settop2bottext()
+{
+ top2bot.setattr("text",
+ sys->sprint("top to bottom (%d left)", top2botcount), All);
+}
+
+startclique()
+{
+ addlayobj, addlayframe: import cardlib;
+
+ entry := clique.newobject(nil, All, "widget entry");
+ entry.setattr("command", "say", All);
+ addlayobj("entry", nil, nil, dTOP|FILLX, entry);
+ addlayframe("arena", nil, nil, dTOP|EXPAND|FILLX|FILLY, dTOP);
+
+ addlayframe("top", "arena", nil, dTOP|EXPAND, dTOP);
+ addlayframe("mid", "arena", nil, dTOP|EXPAND, dTOP);
+ addlayframe("bot", "arena", nil, dTOP|EXPAND, dTOP);
+
+ sevens = array[7] of {* => newstack(nil, Openspec, "open")};
+ acepiles = array[4] of {* => newstack(nil, Untitledpilespec, "acepile")};
+ spare1 = newstack(nil, Untitledpilespec, "spare1");
+ spare2 = newstack(nil, Untitledpilespec, "spare2");
+
+ cardlib->makecards(spare1, (0, 13), nil);
+
+ for (i := 0; i < 4; i++)
+ addlayobj(nil, "top", nil, dRIGHT, acepiles[i]);
+ for (i = 0; i < len sevens; i++)
+ addlayobj(nil, "mid", nil, dLEFT|oDOWN|EXPAND, sevens[i]);
+ addlayframe("buts", "bot", nil, dLEFT|EXPAND|aUPPERRIGHT, dTOP);
+ top2bot = newbutton("top2bot", "top to bottom");
+ addlayobj(nil, "buts", nil, dTOP, top2bot);
+ addlayobj(nil, "buts", nil, dTOP, newbutton("redeal", "redeal"));
+ addlayobj(nil, "bot", nil, dLEFT, spare1);
+ addlayobj(nil, "bot", nil, dLEFT|EXPAND|aCENTRELEFT, spare2);
+ deal();
+ settop2bottext();
+}
+
+clearup()
+{
+ for (i := 0; i < len sevens; i++)
+ cardlib->discard(sevens[i], spare1, 1);
+ for (i = 0; i < len acepiles; i++)
+ cardlib->discard(acepiles[i], spare1, 1);
+ cardlib->discard(spare2, spare1, 1);
+}
+
+deal()
+{
+ cardlib->shuffle(spare1);
+
+ for (i := 0; i < 7; i++) {
+ spare1.transfer((0, i + 1), sevens[i], 0);
+ cardlib->setface(sevens[i].children[i], 1);
+ }
+
+}
+
+newbutton(cmd, text: string): ref Object
+{
+ but := clique.newobject(nil, All, "widget button");
+ but.setattr("command", cmd, All);
+ but.setattr("text", text, All);
+ return but;
+}
+
+newstack(parent: ref Object, spec: Stackspec, stype: string): ref Object
+{
+ stack := cardlib->newstack(parent, nil, spec);
+ stack.setattr("type", stype, None);
+ stack.setattr("actions", "click", All);
+ return stack;
+}
+
+getcard(card: ref Object): Card
+{
+ c := cardlib->getcard(card);
+ c.number = rank[c.number];
+ return c;
+}
+
+isred(c: Card): int
+{
+ return c.suit == Cardlib->DIAMONDS || c.suit == Cardlib->HEARTS;
+}
+
+select(cp: ref Cmember, stack: ref Object, r: Range)
+{
+ if (cp.sel.isempty()) {
+ cp.sel.set(stack);
+ cp.sel.setrange(r);
+ } else {
+ if (cp.sel.r.start == r.start && cp.sel.r.end == r.end)
+ cp.sel.set(nil);
+ else
+ cp.sel.setrange(r);
+ }
+}