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