summaryrefslogtreecommitdiff
path: root/appl/spree/engines/afghan.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/spree/engines/afghan.b')
-rw-r--r--appl/spree/engines/afghan.b302
1 files changed, 302 insertions, 0 deletions
diff --git a/appl/spree/engines/afghan.b b/appl/spree/engines/afghan.b
new file mode 100644
index 00000000..0ca2ca49
--- /dev/null
+++ b/appl/spree/engines/afghan.b
@@ -0,0 +1,302 @@
+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, rand: import spree;
+include "allow.m";
+ allow: Allow;
+include "cardlib.m";
+ cardlib: Cardlib;
+ Selection, Cmember: import cardlib;
+ dTOP, dLEFT, oLEFT, oRIGHT, EXPAND, FILLX, FILLY, aUPPERCENTRE,
+ Stackspec: import Cardlib;
+include "../gather.m";
+
+CLICK, REDEAL: con iota;
+
+clique: ref Clique;
+rows: array of ref Object; # [10]
+central: array of ref Object; # [4]
+chokey, deck: ref Object;
+direction := 0;
+nredeals := 0;
+
+Rowpilespec := Stackspec(
+ "display", # style
+ 10, # maxcards
+ 0, # conceal
+ nil # title
+);
+
+Centralpilespec := Stackspec(
+ "pile",
+ 13,
+ 0,
+ nil
+);
+
+clienttype(): string
+{
+ return "cards";
+}
+
+maxmembers(): int
+{
+ return 1;
+}
+
+readfile(nil: int, nil: big, nil: int): array of byte
+{
+ return nil;
+}
+
+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;
+}
+
+archive()
+{
+ archiveobj := cardlib->archive();
+ allow->archive(archiveobj);
+ cardlib->archivearray(rows, "rows");
+ cardlib->archivearray(central, "central");
+ cardlib->setarchivename(chokey, "chokey");
+ cardlib->setarchivename(deck, "deck");
+ archiveobj.setattr("direction", string direction, None);
+ archiveobj.setattr("nredeals", string nredeals, None);
+}
+
+start(members: array of ref Member, archived: int)
+{
+ if (archived) {
+ archiveobj := cardlib->unarchive();
+ allow->unarchive(archiveobj);
+ rows = cardlib->getarchivearray("rows");
+ central = cardlib->getarchivearray("central");
+ chokey = cardlib->getarchiveobj("chokey");
+ deck = cardlib->getarchiveobj("deck");
+ direction = int archiveobj.getattr("direction");
+ nredeals = int archiveobj.getattr("nredeals");
+ } else {
+ p := members[0];
+ Cmember.join(p, -1).layout.lay.setvisibility(All);
+ startclique();
+ allow->add(CLICK, p, "click %o %d");
+ }
+}
+
+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 {
+ REDEAL =>
+ if (nredeals >= 3)
+ return "no more redeals";
+ redeal();
+ nredeals++;
+ 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) {
+ # selecting a card to move
+ if (idx < 0 || idx >= len stack.children)
+ return "invalid index";
+ case stype {
+ "row" or
+ "chokey" =>
+ select(cp, stack, (nc - 1, nc));
+ * =>
+ return "you can't move cards from there";
+ }
+ } else {
+ # selecting a stack to move to.
+ card := cardlib->getcard(sel.stack.children[sel.r.start]);
+ case stype {
+ "central" =>
+ top := cardlib->getcard(stack.children[nc - 1]);
+ if (direction == 0) {
+ if (card.number != (top.number + 1) % 13 &&
+ card.number != (top.number + 12) % 13)
+ return "out of sequence";
+ if (card.suit != top.suit)
+ return "wrong suit";
+ direction = card.number - top.number;
+ } else {
+ if (card.number != (top.number + direction + 13) % 13)
+ return "out of sequence";
+ if (card.suit != top.suit)
+ return "wrong suit";
+ }
+ "row" =>
+ if (nc == 0 || sel.stack.getattr("type") == "chokey")
+ return "you wish!";
+ top := cardlib->getcard(stack.children[nc - 1]);
+ if (card.suit != top.suit)
+ return "wrong suit";
+ if (card.number != (top.number + 1) % 13 &&
+ card.number != (top.number + 12) % 13)
+ return "out of sequence";
+ "chokey" =>
+ if (nc != 0)
+ return "only one card allowed there";
+ * =>
+ return "can't move there";
+ }
+ sel.transfer(stack, -1);
+ }
+ }
+ return nil;
+}
+
+startclique()
+{
+ addlayobj, addlayframe: import cardlib;
+
+ entry := clique.newobject(nil, All, "widget entry");
+ entry.setattr("command", "say", All);
+
+ but := clique.newobject(nil, All, "widget button");
+ but.setattr("text", "Redeal", All);
+ but.setattr("command", "redeal", All);
+ allow->add(REDEAL, Cmember.index(0).p, "redeal");
+
+ addlayframe("topf", nil, nil, dTOP|EXPAND|FILLX|aUPPERCENTRE, dTOP);
+ addlayobj(nil, "topf", nil, dLEFT, but);
+ addlayobj(nil, "topf", nil, dLEFT|EXPAND|FILLX, entry);
+
+ addlayframe("arena", nil, nil, dTOP|EXPAND|FILLX|FILLY, dTOP);
+
+ addlayframe("left", "arena", nil, dLEFT|EXPAND, dTOP);
+ addlayframe("central", "arena", nil, dLEFT|EXPAND, dTOP);
+ addlayframe("right", "arena", nil, dLEFT|EXPAND, dTOP);
+
+ rows = array[10] of {* => newstack(nil, Rowpilespec, "row")};
+ central = array[4] of {* => newstack(nil, Centralpilespec, "central")};
+ chokey = newstack(nil, Centralpilespec, "chokey");
+
+ deck = clique.newobject(nil, All, "stack");
+ cardlib->makecards(deck, (0, 13), nil);
+ cardlib->shuffle(deck);
+
+ for (i := 0; i < 5; i++)
+ addlayobj(nil, "left", nil, dTOP|oRIGHT, rows[i]);
+ for (i = 5; i < 10; i++)
+ addlayobj(nil, "right", nil, dTOP|oRIGHT, rows[i]);
+ for (i = 0; i < 4; i++)
+ addlayobj(nil, "central", nil, dTOP, central[i]);
+ addlayobj(nil, "central", nil, dTOP, chokey);
+
+ for (i = 0; i < 52; i++)
+ cardlib->setface(deck.children[i], 1);
+ # get top card from deck for central piles.
+ c := deck.children[len deck.children - 1];
+ v := cardlib->getcard(c);
+ j := 0;
+ for (i = len deck.children - 1; i >= 0; i--) {
+ w := cardlib->getcard(deck.children[i]);
+ if (w.number == v.number)
+ deck.transfer((i, i + 1), central[j++], -1);
+ }
+ for (i = 0; i < 10; i += 5) {
+ for (j = i; j < i + 4; j++)
+ deck.transfer((0, 5), rows[j], -1);
+ deck.transfer((0, 4), rows[j], -1);
+ }
+}
+
+redeal()
+{
+ for (i := 0; i < len rows; i++)
+ cardlib->discard(rows[i], deck, 0);
+ cardlib->shuffle(deck);
+
+ i = 0;
+ while ((n := len deck.children) > 0) {
+ l, r: int;
+ if (n >= 10)
+ l = r = 5;
+ else {
+ l = n / 2;
+ r = n - l;
+ }
+ deck.transfer((0, l), rows[i], 0);
+ deck.transfer((0, r), rows[i + 5], 0);
+ i++;
+ }
+
+ n = cardlib->nmembers();
+ for (i = 0; i < n; i++)
+ Cmember.index(i).sel.set(nil);
+}
+
+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;
+}
+
+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);
+ }
+}
+
+archivearray(a: array of ref Object, name: string)
+{
+ for (i := 0; i < len a; i++)
+ cardlib->setarchivename(a[i], name + string i);
+}
+
+unarchivearray(a: array of ref Object, name: string)
+{
+ for (i := 0; i < len a; i++)
+ a[i] = cardlib->getarchiveobj(name + string i);
+}