ref: a5cb451b299b03f44154fac5780b6a57ca130ce0
dir: /appl/spree/engines/afghan.b/
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);
}