#include #include #include #include #include #include enum{GAP = 12}; typedef struct Card Card; struct Card{int v; Card *next;}deck[52], *cell[4], *found[4], *tab[8], **from; void setfrom(Card **new){ if(*new == nil) return; from = new; if(cell[0] == nil || cell[1] == nil || cell[2] == nil || cell[3] == nil || tab[0] == nil || tab[1] == nil || tab[2] == nil || tab[3] == nil || tab[4] == nil || tab[5] == nil || tab[6] == nil || tab[7] == nil || (*from)->v % 13 == 0 || (*from)->v == found[0]->v + 1 || (*from)->v == found[1]->v + 1 || (*from)->v == found[2]->v + 1 || (*from)->v == found[3]->v + 1) return; for(new = tab; new <= tab + 7; new++) if((*from)->v % 13 + 1 == (*new)->v % 13 && (*from)->v >= 13 & (*from)->v < 39 ^ (*new)->v >= 13 & (*new)->v < 39) return; from = nil; } int move(Card **to){ Card *tmp; if(to >= cell && to <= cell + 3 && *to != nil || to >= found && to <= found + 3 && (*to == nil && (*from)->v % 13 || *to != nil && ((*from)->v % 13 == 0 || (*from)->v != (*to)->v + 1)) || to >= tab && to <= tab + 7 && *to != nil && ((*from)->v % 13 + 1 != (*to)->v % 13 || ((*from)->v >= 13 && (*from)->v < 39) == ((*to)->v >= 13 && (*to)->v < 39))) return 0; tmp = (*from)->next; (*from)->next = *to; *to = *from; *from = tmp; from = nil; return 1; } void automove(void){ Card **cp; for(cp = found; cp < found + 4; cp++) if(cp != from && move(cp)) return; for(cp = tab; cp < tab + 8; cp++) if(*cp != nil && cp != from && move(cp)) return; for(cp = tab; cp < tab + 8; cp++) if(cp != from && move(cp)) return; for(cp = cell; cp < cell + 4; cp++) if(cp != from && move(cp)) return; from = nil; } void threadmain(int, char**){ Keyboardctl *kbd; Mousectl *m; Image *bg, *fg, *cards; Card **cp, *c; Rune k; Rectangle r; int w, h, i, j, b; 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, "freecell") < 0) sysfatal("initdraw: %r"); r = Rect(0,0,1,1); bg = allocimage(display, r, RGBA32, 1, DDarkgreen); fg = allocimage(display, r, RGBA32, 1, DPaleyellow); if(bg == nil || fg == nil) sysfatal("allocimage: %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; 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 < 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; } for(i = 0; i < 8; i++) tab[i] = deck + i; for(; i < 52; i++) deck[i - 8].next = deck + i; 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 - 8 * w - 7 * GAP >> 1); j = m->xy.y - screen->r.min.y - GAP; if(i < 0 || j < 0 || i % (w + GAP) >= w || (i /= w + GAP) >= 8) from = nil; else{ if(j < h){ cp = i < 4 ? cell + i : found + i - 4; if(b & 6) from = cp; if(from == cp) automove(); else if(from == nil) setfrom(cp); else move(cp); } else if(j < h + GAP) from = nil; else if(tab[i] == nil){ if(j < 2 * h + GAP && from != nil) move(tab + i); from = nil; } else{ for(c = tab[i]; c->next != nil; c = c->next) j -= h / 4; if(j < h + GAP || j >= 2 * h + GAP) from = nil; else{ if(b & 6) from = tab + i; if(tab + i == from) automove(); else if(from == nil) setfrom(tab + i); else move(tab + i); } } } if(0) case REDRAW: 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 + 8 * w + 9 * GAP >> 1; r.min.y = screen->r.min.y + GAP; r.max.y = r.min.y + h; for(i = 0, cp = found + 3; i < 8; i++){ r.max.x = r.min.x - GAP; r.min.x = r.max.x - w; if(*cp == nil) border(screen, r, 1, fg, ZP); else draw(screen, r, cards, nil, Pt((*cp)->v % 13 * w, (*cp)->v / 13 * h)); if(from == cp) border(screen, r, GAP / -2, fg, ZP); cp = cp == found ? cell + 3 : cp - 1; } for(i = 0; i < 8; i++, r.min.x = r.max.x + GAP){ r.max.x = r.min.x + w; r.min.y = screen->r.min.y + h + 2 * 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 = c->next) r.min.y += h / 4; r.max.y = r.min.y + h; if(from == tab + i) border(screen, r, GAP / -2, fg, ZP); for(c = tab[i]; c != nil; c = c->next){ draw(screen, r, cards, nil, Pt(c->v % 13 * w, c->v / 13 * h)); r.max.y = r.min.y; r.min.y = r.max.y - h / 4; } } 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("evpla laxa landicosa"); } }