ref: aced86644be1b395e0ef2d622749c1aaf1b2d9dc
dir: /nordle.c/
#include <u.h> #include <libc.h> #include <bio.h> #include <ctype.h> #include <thread.h> #include <draw.h> #include <mouse.h> #include <keyboard.h> enum { BACK, TEXT, BORD, WPOS, RPOS, NONE, NCOLS, }; enum { Maxwords = 4096, Padding = 4, }; enum { Mnewgame, Mexit, }; char *menu3str[] = { "new game", "exit", nil }; Menu menu3 = { menu3str }; Mousectl *mc; Keyboardctl *kc; Font *lfont; Font *kfont; Image *cols[NCOLS]; Point ls; Point l0; Point k0; char *words[Maxwords]; int nwords; char *word; int lcount; int lnum; char lines[5][6]; int tried[26]; int done; int won; int stats[2] = {0}; int validword(char *w) { if(strlen(w) != 5) return 0; while(*w){ if(*w < 'a' || *w > 'z') return 0; w++; } return 1; } void readwords(char *filename) { Biobuf *bp; char *s; int l; bp = Bopen(filename, OREAD); if(bp == nil) sysfatal("Bopen: %r"); nwords = 0; for(l = 1;; l++){ s = Brdstr(bp, '\n', 1); if(s == nil) break; if(!validword(s)){ fprint(2, "invalid word '%s' at line %d\n", s, l); threadexitsall("invalid word"); } words[nwords++] = s; if(nwords == Maxwords) break; } Bterm(bp); if(nwords == 0){ fprint(2, "empty wordlist\n"); threadexitsall("empty wordlist"); } } int wordexists(void) { char w[6] = {0}; int i; for(i = 0; i < 5; i++) w[i] = lines[i][lnum]; for(i = 0; i < nwords; i++) if(strncmp(words[i], w, 5) == 0) return 1; return 0; } void newgame(void) { done = 0; won = 0; lcount = -1; lnum = 0; memset(tried, 0, 26*sizeof(uint)); memset(lines, 0, 30*sizeof(char)); word = words[nrand(nwords)]; } int foundword(void) { int i; for(i = 0; i < 5; i++) if(lines[i][lnum - 1] != word[i]) return 0; return 1; } int trystate(int col, int row) { int s; if(lines[col][row] == word[col]) s = RPOS; else if(strchr(word, lines[col][row]) != nil) s = WPOS; else s = NONE; return s; } void showmessage(char *message) { Point p; int w; p = screen->r.min; p.y += Padding + 1.5*kfont->height; w = stringwidth(kfont, message); p.x += (Dx(screen->r) - w) / 2; string(screen, p, cols[WPOS], ZP, kfont, message); flushimage(display, 1); } void redraw(void) { int i, j, c; Point p, p1, pl; Rectangle br; char m[255], s[2] = {0}; draw(screen, screen->r, cols[BACK], nil, ZP); snprint(m, sizeof m, "%d/%d games won", stats[0], stats[1]); string(screen, addpt(screen->r.min, Pt(Padding, Padding)), cols[BORD], ZP, font, m); if(done){ p = screen->r.min; p.y += Padding + 1.5*kfont->height; if(won){ snprint(m, sizeof m, "congratulations"); c = RPOS; }else{ snprint(m, sizeof m, "word was: %s", word); c = TEXT; } i = stringwidth(kfont, m); p.x += (Dx(screen->r) - i) / 2; string(screen, p, cols[c], ZP, kfont, m); } for(j = 0; j < 6; j++){ for(i = 0; i < 5; i++){ p = Pt(l0.x + i*(ls.x + Padding), l0.y + j*(ls.y + Padding)); p1 = Pt(p.x + ls.x, p.y + ls.y); br = Rpt(p, p1); if(lnum < j || (lnum == j && lcount != 5)) border(screen, br, 1, cols[BORD], ZP); if(lnum > j){ c = trystate(i, j); draw(screen, br, cols[c], nil, ZP); } if(lnum > j || (lnum == j && lcount >= i)){ pl.x = p.x + (Dx(br) - ls.x/2) / 2; pl.y = p.y + (Dy(br) - ls.y/2) / 2; s[0] = toupper(lines[i][j]); stringn(screen, pl, cols[TEXT], ZP, lfont, s, 1); } } } j = 0; p = k0; for(i = 0; i < 26; i++){ if(tried[i] == 0) continue; c = tried[i] == 1 ? NONE : RPOS; s[0] = 'A' + i; p = stringnbg(screen, p, cols[TEXT], ZP, kfont, s, 1, cols[c], ZP); p.x += 4; j += 1; if(j == 13){ j = 0; p.x = k0.x; p.y += kfont->height; } } flushimage(display, 1); } void eresize(void) { redraw(); } void emouse(Mouse *m) { if(m->buttons != 4) return; switch(menuhit(3, mc, &menu3, nil)){ case Mnewgame: newgame(); redraw(); break; case Mexit: threadexitsall(nil); } } void ekeyboard(Rune k) { int i; char c; if(k == Kdel) threadexitsall(nil); if(done){ if(k == '\n'){ newgame(); redraw(); } return; } if(lcount < 4 && 'a' <= k && k <= 'z'){ lines[++lcount][lnum] = (char)k; redraw(); }else if(k == Kbs){ if(lcount >= 0){ lcount -= 1; redraw(); } }else if(k == '\n'){ if(lcount == 4){ if(!wordexists()){ lcount = -1; redraw(); showmessage("invalid word"); return; } for(i = 0; i < 5; i++){ c = lines[i][lnum]; tried[c - 'a'] = 1 + (strchr(word, c) != nil); } ++lnum; lcount = -1; if(foundword()){ stats[1]++; stats[0]++; done = 1; won = 1; }else if(lnum == 6){ stats[1]++; done = 1; won = 0; } redraw(); } } } void initcols(void) { cols[BACK] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x121213ff); cols[TEXT] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xd7dadcff); cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x818384ff); cols[WPOS] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xb59f3bff); cols[RPOS] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x538d4eff); cols[NONE] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x3a3a3cff); } void initfont(void) { lfont = openfont(display, "/lib/font/bit/lucidasans/typeunicode.16.font"); if(lfont == nil) sysfatal("openfont: %r"); kfont = font; } void initsize(void) { Point p; int w, h, lw, lh, wfd, n; char buf[255]; p = mulpt(stringsize(lfont, " "), 2); lw = 5*(p.x + Padding); lh = 6*(p.y + Padding); w = 2*(p.x + Padding) + lw; h = Padding + 3*kfont->height + lh + Padding + 3*kfont->height + Padding; ls = p; l0 = Pt(screen->r.min.x + Padding + p.x, screen->r.min.y + Padding + 3*kfont->height); k0 = Pt(screen->r.min.x + Padding, screen->r.max.y - Padding - 2*kfont->height); wfd = open("/dev/wctl", OWRITE); if(wfd < 0) sysfatal("open: %r"); n = snprint(buf, sizeof buf, "resize -dx %d -dy %d", w, h); write(wfd, buf, n); close(wfd); } void usage(void) { fprint(2, "usage: %s <filename>\n", argv0); threadexitsall("usage"); } void threadmain(int argc, char *argv[]) { enum { Emouse, Eresize, Ekeyboard }; Mouse m; Rune k; Alt a[] = { { nil, &m, CHANRCV }, { nil, nil, CHANRCV }, { nil, &k, CHANRCV }, { nil, nil, CHANEND }, }; ARGBEGIN{ }ARGEND; if(argc != 1) usage(); readwords(argv[0]); //srand(31337); srand(time(0)); newgame(); if(initdraw(nil, nil, argv0) < 0) sysfatal("initdraw: %r"); if((mc = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); if((kc = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); a[Emouse].c = mc->c; a[Eresize].c = mc->resizec; a[Ekeyboard].c = kc->c; initfont(); initcols(); initsize(); redraw(); for(;;){ switch(alt(a)){ case Emouse: emouse(&m); break; case Eresize: if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); initsize(); eresize(); break; case Ekeyboard: ekeyboard(k); break; } } }