#include #include #include #include #include #include typedef struct Card Card; struct Card{int v; Card *next;}deck[52], *stock, *waste, *found[4], *tab[7], **from; enum{GAP = 12}; int foundation(Card **to){ Card *tmp; if((*from)->v % 13 == 0 && *to != nil || (*from)->v % 13 && (*to == nil || (*from)->v != (*to)->v + 1)) return 0; tmp = *to; *to = *from; *from = (*from)->next; if(*from != nil && (*from)->v < 0) (*from)->v += 52; (*to)->next = tmp; return 1; } int tableau(Card **to){ Card *c, *tmp; int rank; rank = *to == nil ? 12 : (*to)->v % 13 - 1; if(rank < 0 || (*from)->v % 13 != rank && (from == &waste || from >= found && from <= found + 3)) return 0; for(c = *from; c->v % 13 != rank && c->next != nil && c->next->v > 0;) c = c->next; if(c->v % 13 != rank || rank != 12 && (c->v >= 13 & c->v < 39) == ((*to)->v >= 13 & (*to)->v < 39)) return 0; tmp = c->next; if(tmp != nil && tmp->v < 0) tmp->v += 52; c->next = *to; *to = *from; *from = tmp; return 1; } void automove(void){ int i; for(i = 0; i < 4; i++) if(foundation(found + i)) return; for(i = 0; i < 7 && !tableau(tab + i); i++) ; } void threadmain(int, char**){ Mousectl *m; Keyboardctl *kbd; Card *c; Rune k; Rectangle r; Image *bg, *fg, *tmp, *back, *cards; int w, h, i, j, b; char *s = "restocks: 0"; enum{RESIZE, MOUSE, KEYBOARD}; Alt a[] = { [RESIZE]{nil, nil, CHANRCV}, [MOUSE]{nil, nil, CHANRCV}, [KEYBOARD]{nil, &k, CHANRCV}, {nil, nil, CHANEND} }; if(initdraw(nil, nil, "patience") < 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[RESIZE].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 < 52; j++) deck[j].v = j; while(j > 1){ i = nrand(j--); b = deck[i].v; deck[i].v = deck[j].v; deck[j].v = b; } stock = deck; for(i = 0; i < 22; i++) deck[i].next = deck + i + 1; waste = deck + ++i; for(j = 0; j < 7; j++){ tab[j] = deck + ++i; for(b = 0; b < j; b++){ deck[i].next = deck + i + 1; 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 - 7 * w - 6 * GAP >> 1); j = m->xy.y - screen->r.min.y - font->height - 2 * GAP; if(i < 0 || j < 0 || j < h + GAP && j >= h || i % (w + GAP) >= w) from = nil; else if(j < h) switch(i /= w + GAP){ case 0: from = nil; if(stock == nil){ if(waste == nil) break; s[10] = s[10] == '9' ? 'A' : s[10] + 1; while(waste != nil){ c = waste->next; waste->next = stock; stock = waste; waste = c; } } c = stock->next; stock->next = waste; waste = stock; stock = c; break; case 1: if(from == &waste || b & 6){ from = &waste; automove(); from = nil; } else from = waste == nil ? nil : &waste; break; case 3: case 4: case 5: case 6: i -= 3; if(b & 6) from = found[i] == nil ? nil : found + i; if(from == found + i) automove(); else if(from != nil) foundation(found + i); else if(found[i] != nil){ from = found + i; break; } default: from = nil; break; } else{ i /= w + GAP; if(i >= 7 || tab[i] == nil && (from == nil || j >= 2 * h + GAP)) from = nil; else if(tab[i] == nil){ tableau(tab + i); from = nil; } else{ for(c = tab[i]; c->next != nil; c = c->next) j -= c->next->v < 0 ? h / 8 : h / 4; if(j >= 2 * h + GAP) from = nil; else if(from == tab + i || b & 6){ from = tab + i; automove(); from = nil; } else if(from == nil) from = tab + i; else{ tableau(tab + i); from = nil; } } } if(0) case RESIZE: if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); draw(screen, screen->r, bg, nil, ZP); r.min.x = screen->r.min.x + screen->r.max.x - 7 * w - 6 * GAP >> 1; r.min.y = screen->r.min.y + GAP; string(screen, r.min, fg, ZP, font, s); r.min.y += font->height + GAP; r.max.x = r.min.x + w; r.max.y = r.min.y + h; if(stock != nil) draw(screen, r, back, nil, ZP); else border(screen, r, 1, fg, ZP); r.min.x = r.max.x + GAP; r.max.x = r.min.x + w; if(waste == nil) border(screen, r, 1, fg, ZP); else{ draw(screen, r, cards, nil, Pt(waste->v % 13 * w, waste->v / 13 * h)); if(from == &waste) border(screen, r, -GAP / 2, fg, ZP); } r.max.x += w + GAP; for(i = 0; i < 4; 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)); if(from == found + i) border(screen, r, -GAP / 2, fg, ZP); } for(i = 6; i >= 0; i--){ r.min.y = screen->r.min.y + font->height + h + 3 * GAP; if(tab[i] == nil){ r.max.y = r.min.y + h; border(screen, r, 1, fg, ZP); } else{ for(c = tab[i]; c->next != nil; c = c->next) r.min.y += c->next->v < 0 ? h / 8 : h / 4; r.max.y = r.min.y + h; draw(screen, r, cards, nil, Pt(tab[i]->v % 13 * w, tab[i]->v / 13 * h)); if(from == tab + i){ r.min.y = r.max.y; r.max.y = r.min.y + GAP / 2; draw(screen, r, fg, nil, ZP); r.min.y -= h; } for(c = tab[i]->next; c != nil; c = c->next){ r.max.y = r.min.y; if(c->v < 0){ r.min.y = r.max.y - h / 8; draw(screen, r, back, nil, ZP); } else{ r.min.y = r.max.y - h / 4; draw(screen, r, cards, nil, Pt(c->v % 13 * w, c->v / 13 * h)); } } } r.max.x = r.min.x - GAP; r.min.x = r.max.x - w; } 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; } break; default: sysfatal("Apollinaris medicus Titi Impe hic cacavit bene"); } }