diff options
Diffstat (limited to 'appl/spree/engines/afghan.b')
| -rw-r--r-- | appl/spree/engines/afghan.b | 302 |
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); +} |
