💾 Archived View for runjimmyrunrunyoufuckerrun.com › src › games › draughts.c captured on 2021-12-17 at 13:26:06.
-=-=-=-=-=-=-
#include <u.h> #include <libc.h> #include <draw.h> #include <event.h> #include <cursor.h> enum{ BLACK, WHITE, LSQ, BAIZE, DSQ, OUTLINE, HSQ, HUES, HL = 2, MAN = 4, KING = 8, ⛂ = 4, ⛀ = 5, ⛃ = 8, ⛁ = 9, ∅ = 16, ☠ = 49, ⭦ = -5, ⭧ = -4, ⭩ = 4, ⭨ = 5, ∞ = 0x7fffffff, Enine = 8, SQ = 50, RAD = 20, MSGLEN = 128, MSGS = 5 }; ulong rgb[HUES] = { [BLACK] DRed, [WHITE] DWhite, [BAIZE] DDarkgreen, [OUTLINE] DBlack, [DSQ] DYellowgreen, [LSQ] DPaleyellow, [HSQ] DPalegreygreen }; Cursor ⌛ = {{-1, -1}, {0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00, 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0, 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff}, {0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0, 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00}}; char sq[] = { ☠, ☠, ☠, ☠, ☠, ⛀, ⛀, ⛀, ⛀, ⛀, ⛀, ⛀, ⛀, ☠, ⛀, ⛀, ⛀, ⛀, ∅, ∅, ∅, ∅, ☠, ∅, ∅, ∅, ∅, ⛂, ⛂, ⛂, ⛂, ☠, ⛂, ⛂, ⛂, ⛂, ⛂, ⛂, ⛂, ⛂, ☠, ☠, ☠, ☠, ☠ }; char coordof[] = { 0, 0, 0, 0, 0, 28, 29, 30, 31, 24, 25, 26, 27, 0, 20, 21, 22, 23, 16, 17, 18, 19, 0, 12, 13, 14, 15, 8, 9, 10, 11, 0, 4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0 }; char indexof[] = { 36, 37, 38, 39, 32, 33, 34, 35, 27, 28, 29, 30, 23, 24, 25, 26, 18, 19, 20, 21, 14, 15, 16, 17, 9, 10, 11, 12, 5, 6, 7, 8, 0 }; int direction[] = {⭦, ⭧, ⭩, ⭨}; Rectangle square[32], side[2], msgs, moves; Point centre[32]; Image *hue[HUES]; char p[2][64], buf[4096], msg[MSGS][MSGLEN], *ms, *m, *me, *u; int t, flip, msgn, fd = -1; int aijump(int, int, int, int); int aislide(int, int, int, int); void drawboard(void){ Point o; char *s, *e; int i, j, n; for(i = 0; i < 32; i++){ n = indexof[i]; draw(screen, square[i], hue[DSQ | sq[n] & HL], nil, ZP); if(sq[n] < ∅){ fillellipse(screen, centre[i], RAD, RAD, hue[sq[n] & WHITE], ZP); ellipse(screen, centre[i], RAD, RAD, 0, hue[OUTLINE], ZP); if(sq[n] & KING) ellipse(screen, centre[i], RAD >> 1, RAD >> 1, 0, hue[OUTLINE], ZP); } } draw(screen, moves, hue[BAIZE], nil, ZP); o = screen->r.min; i = t - 10 * SQ / font->height & ~1; if(i < 0){ o.y += i / -2 * font->height; i = 0; } for(j = 0, s = ms; j < i; j++) do ++s; while(s[-1] != ' '); while(s < me && o.y + font->height < moves.max.y){ for(e = s + 4; e[-1] != ' '; e++); o = stringn(screen, o, hue[WHITE << (i < t)], ZP, font, s, e - s); s = e; if(i++ & WHITE) o = Pt(screen->r.min.x, o.y + font->height); } } void drawmsgs(void){ Point o; draw(screen, msgs, hue[WHITE], nil, ZP); for(o = msgs.min; o.y < msgs.max.y; o.y += font->height){ string(screen, o, hue[OUTLINE], ZP, font, msg[msgn]); msgn = (msgn + 1) % MSGS; } } void eresized(int i){ Rectangle r; if(i && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window"); r.max.y = screen->r.min.y + 10 * SQ; if(fd >= 0) r.max.y += MSGS * font->height; if((!i || screen->r.max.y != r.max.y) && (i = open("/dev/wctl", OWRITE)) >= 0){ fprint(i, "resize -dy %d", r.max.y - screen->r.min.y + 2 * Borderwidth); close(i); } r.min.x = screen->r.min.x + screen->r.max.x - 8 * SQ >> 1; r.min.y = screen->r.min.y + SQ; r.max.x = r.min.x + 8 * SQ; r.max.y = r.min.y + 8 * SQ; side[flip] = Rect(r.min.x, r.max.y, r.max.x, r.max.y + SQ); side[!flip] = Rect(r.min.x, screen->r.min.y, r.max.x, r.min.y); moves.min = screen->r.min; moves.max.x = r.min.x; moves.max.y = r.max.y + SQ; if(fd >= 0){ msgs.min.x = moves.min.x; msgs.min.y = moves.max.y; msgs.max = screen->r.max; } if(flip){ centre[0].x = r.max.x - SQ / 2; centre[0].y = r.min.y + SQ / 2; for(i = 1; i < 32; i++){ if(i & 3) centre[i] = Pt(centre[i - 1].x - SQ * 2, centre[i - 1].y); else{ centre[i].x = i & 4 ? r.max.x - SQ * 3 / 2 : r.max.x - SQ / 2; centre[i].y = centre[i - 1].y + SQ; } } } else{ centre[0].x = r.min.x + SQ / 2; centre[0].y = r.max.y - SQ / 2; for(i = 1; i < 32; i++){ if(i & 3) centre[i] = Pt(centre[i - 1].x + SQ * 2, centre[i - 1].y); else{ centre[i].x = i & 4 ? r.min.x + SQ * 3 / 2 : r.min.x + SQ / 2; centre[i].y = centre[i - 1].y - SQ; } } } draw(screen, screen->r, hue[BAIZE], nil, ZP); draw(screen, r, hue[LSQ], nil, ZP); r = Rect(SQ/-2, SQ/-2, SQ/2, SQ/2); for(i = 0; i < 32; i++) square[i] = rectaddpt(r, centre[i]); drawboard(); if(fd >= 0) drawmsgs(); r.min.x = side[flip].min.x + side[flip].max.x - stringwidth(font, p[flip]) >> 1; r.min.y = side[flip].min.y + side[flip].max.y - font->height >> 1; string(screen, r.min, hue[LSQ], ZP, font, p[flip]); r.min.x = side[!flip].min.x + side[!flip].max.x - stringwidth(font, p[!flip]) >> 1; r.min.y = side[!flip].min.y + side[!flip].max.y - font->height >> 1; string(screen, r.min, hue[LSQ], ZP, font, p[!flip]); } void clearhl(void){ int i; for(i = 5; i < 40; i++) sq[i] &= ~HL; } int canjump(int from, int dir){ int enemy; enemy = ~sq[from] & WHITE; return (sq[from] & KING || enemy == WHITE && (dir == ⭦ || dir == ⭧) || enemy == BLACK && (dir == ⭩ || dir == ⭨)) && (sq[from + dir] & ☠) == enemy && sq[from + dir + dir] == ∅; } int jumper(int i){ return canjump(i, ⭦) || canjump(i, ⭧) || canjump(i, ⭩) || canjump(i, ⭨); } int canslide(int from, int dir){ int colour; colour = sq[from] & WHITE; return (sq[from] & KING || colour == BLACK && (dir == ⭦ || dir == ⭧) || colour == WHITE && (dir == ⭩ || dir == ⭨)) && sq[from + dir] == ∅; } void checkgameover(void){ int i, hl, canmove; clearhl(); hl = strcmp(p[t & 1], u) || fd >= 0 && m < me ? 0 : HL; canmove = 0; for(i = 5; i < 40; i++) if(!(sq[i] & ☠ ^ t & WHITE) && jumper(i) && ++canmove) sq[i] |= hl; if(!canmove) for(i = 5; i < 40; i++) if(!(sq[i] & ☠ ^ t & WHITE) && (canslide(i, ⭦) || canslide(i, ⭧) || canslide(i, ⭩) || canslide(i, ⭨)) && ++canmove) sq[i] |= hl; if(canmove || fd < 0) return; me = seprint(me, buf + sizeof(buf), "# %s vs %s: %s won\n", p[0], p[1], p[~t & 1]); write(fd, m, me - m); close(fd); write(1, buf, me - buf); exits(nil); } int aisubjump(int d, int α, int β, int from, char *stash){ int i, to, prisoner, val; for(i = to = 0; α < β && i < 4; i++) if(canjump(from, direction[i])){ to = from + direction[i] * 2; prisoner = sq[from + direction[i]]; sq[to] = sq[from]; sq[from] = ∅; sq[from + direction[i]] = ∅; val = aisubjump(d, α, β, to, seprint(stash, stash + 4, "%c%d", prisoner & KING ? 'X' : 'x', coordof[to] + 1)); if(val > α) α = val; sq[from] = sq[to]; sq[to] = ∅; sq[from + direction[i]] = prisoner; } if(to) return α; if(sq[from] & MAN && (from > 35 || from < 9)){ *stash++ = 'K'; sq[from] ^= MAN | KING; } *stash++ = ' '; *stash = 0; t++; val = -aijump(d - 1, -β, -α, 0); t--; if(stash[-2] == 'K') sq[from] ^= MAN | KING; return val; } int aijump(int d, int α, int β, int stashit){ int i, val; char stash[40], *s; s = t & WHITE ? stash : seprint(stash, stash + 8, "%d.", t + 2 >> 1); *s = 0; for(i = 5; α < β && i < 40; i++) if(!(sq[i] & ☠ ^ t & WHITE) && jumper(i)){ val = aisubjump(d, α, β, i, seprint(s, s + 3, "%d", coordof[i] + 1)); if(val > α){ α = val; if(stashit) strecpy(me, buf + sizeof(buf), stash); } } return *s ? α : aislide(d, α, β, stashit); } int aislide(int d, int α, int β, int stashit){ int i, from, to, promote, val; if(d < 1){ /* evaluate */ val = 0; from = t & WHITE; /* material advantage; not too sophisticated */ for(i = 5; i < 40; i++){ if((sq[i] & ☠) == from){ val += (sq[i] & (MAN | KING)) << 7; } else if((sq[i] & ☠) == !from){ val -= (sq[i] & (MAN | KING)) << 7; } } return val | rand() & 0xf; } to = 0; for(from = 5; α < β && from < 40; from++) if(!(sq[from] & ☠ ^ t & WHITE)) for(i = 0; α < β && i < 4; i++) if(canslide(from, direction[i])){ to = from + direction[i]; promote = sq[from] & MAN && (to > 35 || to < 9) ? MAN | KING : 0; sq[to] = sq[from] ^ promote; sq[from] = ∅; t++; val = -aijump(d - 1, -β, -α, 0); t--; sq[from] = sq[to] ^ promote; sq[to] = ∅; if(val > α){ α = val; if(stashit){ if(t & WHITE) seprint(me, buf + sizeof(buf), "%d-%d%s", coordof[from] + 1, coordof[to] + 1, promote ? "K " : " "); else seprint(me, buf + sizeof(buf), "%d.%d-%d%s", t + 2 >> 1, coordof[from] + 1, coordof[to] + 1, promote ? "K " : " "); } } } return to ? α : -∞; } int getsq(char **sp){ int i; i = **sp - '0'; if(i < 1 || i > 9) return 0; if(sp[0][1] >= '0' && sp[0][1] <= '9'){ i = i * 10 + sp[0][1] - '0'; if(i < 1 || i > 32) return 0; } *sp += (i > 9) + 1; return indexof[i - 1]; } char* fwd(void){ int seq[10], i; char *s; if(m >= me) return "at end of movetext"; if(*m == '#'){ write(1, buf, me - buf); exits(nil); } s = m; if(~t & WHITE){ while(*s >= '0' && *s <= '9') s++; if(*s++ != '.') return "bad move number indication"; } seq[0] = getsq(&s); if(!seq[0] || sq[seq[0]] & ☠ ^ t & WHITE) return "bad source square"; if(*s == '-'){ s++; seq[1] = getsq(&s); if(!seq[1] || sq[seq[1]] != ∅) return "bad destination square"; if(*s == 'K'){ s++; if(*s++ != ' ') return "no terminating space"; sq[seq[0]] ^= MAN | KING; } else if(*s++ != ' ') return "no terminating space"; sq[seq[1]] = sq[seq[0]]; sq[seq[0]] = ∅; t++; m = s; return nil; } if(*s == 'x' || *s == 'X'){ i = 0; do{ s++; seq[++i] = getsq(&s); if(!seq[i] || sq[seq[i]] != ∅ || sq[seq[i - 1] + seq[i] >> 1] & ☠ ^ ~t & WHITE) return "bad jump"; }while(*s == 'x' || *s == 'X'); if(*s == 'K'){ s++; if(*s++ != ' ') return "no terminating space"; sq[seq[0]] ^= MAN | KING; } else if(*s++ != ' ') return "no terminating space"; sq[seq[i]] = sq[seq[0]]; sq[seq[0]] = ∅; while(i--) sq[seq[i] + seq[i + 1] >> 1] = ∅; t++; m = s; return nil; } return "bad move indicator"; } void bwd(void){ int from, to, last, prisoner; char *s; do --m; while(m[-1] != ' '); s = m; if(t & WHITE) while(*s++ != '.'); from = to = getsq(&s); if(*s == '-'){ s++; to = getsq(&s); } else do{ last = to; prisoner = (*s++ == 'x' ? MAN : KING) | t & WHITE; to = getsq(&s); sq[last + to >> 1] = prisoner; }while(*s == 'x' || *s == 'X'); sq[from] = sq[to] ^ (*s == 'K' ? MAN | KING : 0); sq[to] = ∅; t--; } void main(int argc, char **argv){ Event e; char *s; int i, j; u = getuser(); strcpy(p[BLACK], u); strcpy(p[WHITE], u); m = ms = me = seprint(buf, buf + sizeof(buf), "%s ", argv[0]); ARGBEGIN{ default: sysfatal("usage: %s [-9 file | movetext]", argv0); case '9': m = s = EARGF(sysfatal("bad argument to -9")); if(strncmp(s, "draughts~", 9)) sysfatal("filename must start with draughts~"); while(*s++ != '~'); for(i = 0; i < 63 && s[i] != '~'; i++) p[0][i] = s[i]; if(i == 63) sysfatal("bad filename format: player separator ~ not found"); p[0][i] = 0; s += i + 1; for(i = 0; i < 63 && s[i]; i++) p[1][i] = s[i]; if(i == 63) sysfatal("bad filename format: player separator ~ not found"); p[1][i] = 0; if((fd = create(m, ORDWR, 0666)) < 0) sysfatal("create: %r"); i = read(fd, buf, sizeof(buf)); if(i == 0){ if(write(fd, buf, me - buf) != me - buf) sysfatal("write: %r"); m = me; break; } if(i < 1 || i >= sizeof(buf)) sysfatal("read %d bytes: not a 9p draughts file?", i); me = buf + i; for(m = buf; m < me && *m != ' '; m++); if(m == me) sysfatal("no space character found: not a 9p draughts file?"); ms = ++m; break; }ARGEND if(argc){ if(fd >= 0) sysfatal("usage: %s [-9 file | movetext]", argv0); for(i = 0; i < argc; i++) me = seprint(me, buf + sizeof(buf), "%s ", argv[i]); } while((s = fwd()) == nil); if(m != me) sysfatal("bad movetext: %s: %s", s, m); if(fd < 0) s = argv0; else{ s = me; seprint(s, buf + sizeof(buf), "%s: %s vs %s", argv0, p[0], p[1]); } if(initdraw(nil, nil, s) < 0) sysfatal("initdraw: %r"); for(i = 0; i < HUES; i++) if((hue[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, rgb[i])) == nil) sysfatal("allocimage: %r"); einit(Emouse | Ekeyboard); if(fd >= 0){ estart(Enine, fd, 256); if(strcmp(u, p[t & 1])) esetcursor(&⌛); } srand(truerand()); checkgameover(); if(!strcmp(u, p[1]) && strcmp(u, p[0])) flip = 1; eresized(0); for(;;){ if(fd < 0 && m == me && !strncmp(p[t & 1], "ai level ", 9) && !p[t & 1][10]){ flushimage(display, 1); if(ecankbd() || me + 33 >= buf + sizeof(buf)){ while(ecankbd()) ekbd(); /* gulp */ strcpy(p[t & 1] + 10, " interrupted"); checkgameover(); eresized(0); continue; } esetcursor(&⌛); *me = 0; aijump(p[t & 1][9] - '0' + 1, -∞, ∞, 1); sleep(400); esetcursor(nil); if(*me == 0){ strcpy(p[t & 1] + 10, " resigned"); eresized(0); continue; } while(*me) me++; fwd(); checkgameover(); } else switch(event(&e)){ case Ekeyboard: switch(e.kbdc){ case 127: s = seprint(me, buf + sizeof(buf), "# %s vs %s\n", p[0], p[1]); write(1, buf, s - buf); exits(nil); case 0xf011: if(m == ms) continue; bwd(); checkgameover(); break; case 0xf012: fwd(); checkgameover(); break; case 0xf00e: while(m > ms) bwd(); checkgameover(); break; case 0xf800: while(fwd() == nil); checkgameover(); break; case 0x1b: flip ^= 1; eresized(0); continue; default: if(fd < 0) continue; i = me + MSGLEN > buf + sizeof(buf) ? buf + sizeof(buf) - me : MSGLEN; me[0] = e.kbdc; me[1] = 0; i = eenter("message", me, i, &e.mouse); if(i < 1 || me[i - 1] == ' ') continue; if(!strcmp(u, p[t & 1]) && (!strcmp(me, "draw!") || !strcmp(me, "resign!"))){ s = seprint(me, buf + sizeof(buf), "# %s %s\n", u, *me == 'd' ? "declared a draw" : "resigned"); write(fd, me, s - me); write(1, buf, s - buf); exits(nil); } if(write(fd, me, i) != i){ snprint(msg[msgn], MSGLEN, "write: %r"); msgn = (msgn + 1) % MSGS; drawmsgs(); } continue; } break; case Emouse: if(e.mouse.buttons & 16){fwd(); checkgameover(); break;} if(e.mouse.buttons & 8 && m > ms){bwd(); checkgameover(); break;} if(!e.mouse.buttons) continue; do eread(Emouse, &e); while(e.mouse.buttons); if((ptinrect(e.mouse.xy, side[i = 0]) || ptinrect(e.mouse.xy, side[i = 1])) && fd < 0){ strcpy(p[i], "ai level "); eenter("ai level ? [0–9]", p[i] + 9, 2, &e.mouse); if(p[i][9] < '1' || p[i][9] > '9') strcpy(p[i], u); eresized(0); continue; } for(i = 0; i < 32 && !ptinrect(e.mouse.xy, square[i]); i++); j = indexof[i]; if(!(sq[j] & HL)) continue; clearhl(); me = t & WHITE ? seprint(m, buf + sizeof(buf), "%d", i + 1) : seprint(m, buf + sizeof(buf), "%d.%d", t + 2 >> 1, i + 1); if(jumper(j)) do{ i = j; if(canjump(j, ⭦)) sq[j + ⭦ + ⭦] |= HL; if(canjump(j, ⭧)) sq[j + ⭧ + ⭧] |= HL; if(canjump(j, ⭩)) sq[j + ⭩ + ⭩] |= HL; if(canjump(j, ⭨)) sq[j + ⭨ + ⭨] |= HL; drawboard(); do{ do eread(Emouse, &e); while(!e.mouse.buttons); do eread(Emouse, &e); while(e.mouse.buttons); for(j = 0; j < 32 && !ptinrect(e.mouse.xy, square[j]); j++); j = indexof[j]; }while(!(sq[j] & HL)); *me++ = sq[i + j >> 1] & KING ? 'X' : 'x'; me = seprint(me, buf + sizeof(buf), "%d", coordof[j] + 1); sq[i + j >> 1] = ∅; sq[j] = sq[i]; sq[i] = ∅; clearhl(); }while(jumper(j)); else{ if(canslide(j, ⭦)) sq[j + ⭦] |= HL; if(canslide(j, ⭧)) sq[j + ⭧] |= HL; if(canslide(j, ⭩)) sq[j + ⭩] |= HL; if(canslide(j, ⭨)) sq[j + ⭨] |= HL; drawboard(); do{ do eread(Emouse, &e); while(!e.mouse.buttons); do eread(Emouse, &e); while(e.mouse.buttons); for(i = 0; i < 32 && !ptinrect(e.mouse.xy, square[i]); i++); i = indexof[i]; }while(!(sq[i] & HL)); me = seprint(me, buf + sizeof(buf), "-%d", coordof[i] + 1); sq[i] = sq[j]; sq[j] = ∅; j = i; } if(sq[j] & MAN && (j > 35 || j < 9)){ sq[j] ^= MAN | KING; *me++ = 'K'; } *me++ = ' '; t++; checkgameover(); if(fd >= 0){ if(write(fd, m, me - m) != me - m){ write(1, buf, me - buf); sysfatal("write: %r"); } esetcursor(&⌛); do eread(Enine, &e); while(e.n != me - m && strncmp((char*)e.data, m, e.n)); } m = me; break; case Enine: if(e.n < 1) continue; if(e.data[e.n - 1] != ' ' && e.data[e.n - 1] != '\n'){ i = e.n < MSGLEN ? e.n : MSGLEN - 1; msg[msgn][i] = 0; while(i--) msg[msgn][i] = e.data[i]; msgn = (msgn + 1) % MSGS; drawmsgs(); continue; } if(me + e.n >= buf + sizeof(buf)){ strcpy(msg[msgn], "length of received move would overflow buffer!"); msgn = (msgn + 1) % MSGS; drawmsgs(); continue; } memmove(me, e.data, e.n); me += e.n; while(fwd() == nil); checkgameover(); if(!strcmp(p[t & 1], u)) esetcursor(nil); } drawboard(); } }