#include #include #include #include #include #include enum{GAP = 12}; typedef struct Card Card; struct Card{int v; Card *next;} deck[104], *found[8], *tab[10], **fromtab, *from, *c; void threadmain(int, char**){ Keyboardctl *kbd; Mousectl *m; Image *bg, *fg, *tmp, *back, *cards; Rune k; Rectangle r; int w, h, i, j, b, stock = 5; enum{REDRAW, MOUSE, KEYBOARD}; Alt a[] = { [REDRAW]{nil, nil, CHANRCV}, [MOUSE]{nil, nil, CHANRCV}, [KEYBOARD]{nil, &k, CHANRCV}, {nil, nil, CHANEND} }; if(initdraw(nil, nil, "spider") < 0) sysfatal("initdraw: %r"); if((i = open("/sys/games/lib/cards/default.bit", OREAD)) < 0) sysfatal("couldn't find cards: %r"); if((cards = readimage(display, i, 0)) == nil) sysfatal("readimage: /sys/games/lib/cards/default.bit bad format"); close(i); w = Dx(cards->r) / 13; h = Dy(cards->r) / 4; bg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DDarkgreen); fg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DPaleyellow); tmp = allocimage(display, Rect(0, 0, 4, 4), RGBA32, 1, DBlue); back = allocimage(display, Rect(0, 0, w, h), RGBA32, 0, DNofill); if(bg == nil || fg == nil || tmp == nil || back == nil) sysfatal("allocimage: %r"); draw(tmp, Rect(2, 2, 3, 3), fg, nil, ZP); draw(back, back->r, tmp, nil, ZP); freeimage(tmp); border(back, back->r, 1, fg, ZP); if((m = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); a[REDRAW].c = m->resizec; a[MOUSE].c = m->c; a[MOUSE].v = &m->Mouse; if((kbd = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); a[KEYBOARD].c = kbd->c; srand(truerand()); for(j = 0; j < 104; j++) deck[j].v = j % 52; while(j > 1){ i = nrand(j--); k = deck[i].v; deck[i].v = deck[j].v; deck[j].v = k; } for(i = 0; i < 10; i++) tab[i] = deck + i; for(i = 0; i < 44; i++){ deck[i].next = deck + i + 10; deck[i].v -= 52; } sendul(m->resizec, 1); for(;;) switch(alt(a)){ case MOUSE: if(!m->buttons) break; b = m->buttons; do readmouse(m); while(m->buttons); i = m->xy.x - (screen->r.min.x + screen->r.max.x - 10 * w - 9 * GAP >> 1); j = m->xy.y - screen->r.min.y - GAP; if(i < 0 || j < 0) fromtab = nil; else if(stock && j < h && i < (stock + 3) * w / 4){ deal: for(i = 0; i < 10 && tab[i] != nil; i++) ; if(i == 10){ from = nil; j = 54 + --stock * 10; r.min.x = screen->r.min.x + screen->r.max.x - 10 * w - 9 * GAP >> 1; for(i = 0; 1; i++){ r.max.x = r.min.x + w; r.min.y = screen->r.min.y + GAP + h + GAP + h / 4; for(c = tab[i]; c->next != nil && c->v < 0; c = c->next) r.min.y += h / 8; for(; c->next != nil; c = c->next) r.min.y += h / 4; c->next = deck + j++; if(i == 9) break; r.max.y = r.min.y + h; draw(screen, r, cards, nil, Pt(c->next->v % 13 * w, c->next->v / 13 * h)); flushimage(display, 1); sleep(200); r.min.x = r.max.x + GAP; } } } else if(i % (w + GAP) >= w || (i /= (w + GAP)) >= 10) fromtab = nil; else if(tab[i] == nil){ if(fromtab != nil && j >= h + GAP && j < h + h + GAP){ empty: tab[i] = from; if(*fromtab == from) *fromtab = nil; else{ while((*fromtab)->next != from) fromtab = &(*fromtab)->next; (*fromtab)->next = nil; if((*fromtab)->v < 0) (*fromtab)->v += 52; } } fromtab = nil; } else{ j -= h + GAP; for(c = tab[i]; c->next != nil && c->v < 0; c = c->next) j -= h / 8; if(j < 0) fromtab = nil; else{ for(; c->next != nil && j >= h / 4; c = c->next) j -= h / 4; if(j >= h) fromtab = nil; else if(fromtab != nil && fromtab != tab + i && c->next == nil && c->v % 13 == from->v % 13 + 1){ move: c->next = from; if(*fromtab == from) *fromtab = nil; else{ while((*fromtab)->next != from) fromtab = &(*fromtab)->next; (*fromtab)->next = nil; if((*fromtab)->v < 0) (*fromtab)->v += 52; } while(from->next != nil) from = from->next; if(from->v % 13 == 0){ from = nil; j = 0; for(c = tab[i]; c->next != nil; c = c->next){ if(c->v % 13 && c->v == c->next->v + 1) j++; else{ from = c; j = 0; } } if(j == 12){ for(j = 0; found[j] != nil; j++) ; found[j] = c; if(from == nil) tab[i] = nil; else{ from->next = nil; if(from->v < 0) from->v += 52; } } } fromtab = nil; } else{ if(fromtab == tab + i) b = 6; from = c; fromtab = nil; while(c->next != nil && c->v == c->next->v + 1) c = c->next; if(c->next == nil){ for(j = 0; j < 10; j++){ if(j == i || tab[j] == nil) continue; for(c = tab[j]; c->next != nil; c = c->next) ; if(c->v == from->v + 1 && c->v % 13) break; } if(j == 10) for(j = 0; j < 10; j++){ if(j == i || tab[j] == nil) continue; for(c = tab[j]; c->next != nil; c = c->next) ; if(c->v % 13 == from->v % 13 + 1) break; } if(j == 10) for(j = 0; j < 10; j++){ if(j == i) continue; if(tab[j] == nil){ c = nil; break; } } if(j < 10){ fromtab = tab + i; if(b & 6){ i = j; if(c == nil) goto empty; else goto move; } } } } } } if(0) case REDRAW: if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); draw(screen, screen->r, bg, nil, ZP); r.min.y = screen->r.min.y + GAP; r.max.y = r.min.y + h; if(stock){ r.min.x = screen->r.min.x + screen->r.max.x - 10 * w - 9 * GAP >> 1; for(i = 1; i < stock; i++){ r.max.x = r.min.x + w / 4; draw(screen, r, back, nil, ZP); r.min.x = r.max.x; } r.max.x = r.min.x + w; draw(screen, r, back, nil, ZP); } r.max.x = screen->r.min.x + screen->r.max.x - 6 * w - 7 * GAP >> 1; for(i = 0; i < 8; i++){ r.min.x = r.max.x + GAP; r.max.x = r.min.x + w; if(found[i] == nil) border(screen, r, 1, fg, ZP); else draw(screen, r, cards, nil, Pt(found[i]->v % 13 * w, found[i]->v / 13 * h)); } r.min.x = screen->r.min.x + screen->r.max.x - 10 * w - 9 * GAP >> 1; for(i = 0; i < 10; i++, r.min.x += GAP + w){ r.max.x = r.min.x + w; r.min.y = screen->r.min.y + GAP + h + GAP; if(tab[i] == nil){ r.max.y = r.min.y + h; border(screen, r, 1, fg, ZP); continue; } for(c = tab[i]; c->next != nil && c->v < 0; c = c->next){ r.max.y = r.min.y + h / 8; draw(screen, r, back, nil, ZP); r.min.y = r.max.y; } while(c->next != nil){ r.max.y = r.min.y + h / 4; draw(screen, r, cards, nil, Pt(c->v % 13 * w, c->v / 13 * h)); r.min.y = r.max.y; c = c->next; } r.max.y = r.min.y + h; draw(screen, r, cards, nil, Pt(c->v % 13 * w, c->v / 13 * h)); if(fromtab == tab + i){ for(c = from->next; c != nil; c = c->next) r.min.y -= h / 4; border(screen, r, -GAP >> 1, fg, ZP); } } flushimage(display, 1); break; case KEYBOARD: switch(k){ case Kdel: case 'q': threadexitsall(nil); case Kesc: if((i = open("/dev/wctl", OWRITE)) >= 0){ write(i, "hide", 4); close(i); } break; default: if(stock) goto deal; } break; default: sysfatal("evpla laxa landicosa"); } }