diff --git a/onefile/data/shisen/Arian_Grqi_i_U.ttf b/onefile/data/shisen/Arian_Grqi_i_U.ttf new file mode 100644 index 0000000..afc52b2 Binary files /dev/null and b/onefile/data/shisen/Arian_Grqi_i_U.ttf differ diff --git a/onefile/data/shisen/Vrdznagir.otf b/onefile/data/shisen/Vrdznagir.otf new file mode 100644 index 0000000..6dd1ca1 Binary files /dev/null and b/onefile/data/shisen/Vrdznagir.otf differ diff --git a/onefile/data/shisen/arnamu.ttf b/onefile/data/shisen/arnamu.ttf new file mode 100644 index 0000000..50955e8 Binary files /dev/null and b/onefile/data/shisen/arnamu.ttf differ diff --git a/onefile/data/shisen/letters-gen.png b/onefile/data/shisen/letters-gen.png new file mode 100644 index 0000000..55b7a9a Binary files /dev/null and b/onefile/data/shisen/letters-gen.png differ diff --git a/onefile/data/shisen/letters.gnumeric b/onefile/data/shisen/letters.gnumeric new file mode 100644 index 0000000..c4f868e Binary files /dev/null and b/onefile/data/shisen/letters.gnumeric differ diff --git a/onefile/data/shisen/minas.png b/onefile/data/shisen/minas.png new file mode 100644 index 0000000..f5bddd0 Binary files /dev/null and b/onefile/data/shisen/minas.png differ diff --git a/onefile/data/shisen/wood4.png b/onefile/data/shisen/wood4.png new file mode 100644 index 0000000..b6d90c2 Binary files /dev/null and b/onefile/data/shisen/wood4.png differ diff --git a/onefile/shisen-gen.c b/onefile/shisen-gen.c new file mode 100644 index 0000000..764ec5d --- /dev/null +++ b/onefile/shisen-gen.c @@ -0,0 +1,169 @@ + +#include +#include +#include +#include +#include + +#include "helianthus.h" + + +#define CW 96 +#define CH 128 +#define CR 16 +#define CB 1 +#define SW 8 +#define FS 64 + +#define CWW (CW - 2*CB) +#define CHH (CH - 2*CB) + +const char *letters[][5] = { + {"Ա", "ա", "а", "", "0"}, + {"Բ", "բ", "б", "", "1"}, + {"Գ", "գ", "г", "", "1"}, + {"Դ", "դ", "д", "", "1"}, + {"Ե", "ե", "е", "", "0"}, + {"Զ", "զ", "з", "", "1"}, + {"Է", "է", "э", "", "0"}, + {"Ը", "ը", "ыэ", "", "0"}, + {"Թ", "թ", "тх", "", "3"}, + {"Ժ", "ժ", "ж", "", "1"}, + {"Ի", "ի", "и", "", "0"}, + {"Լ", "լ", "л", "", "1"}, + {"Խ", "խ", "х", "", "2"}, + {"Ծ", "ծ", "тц", "", "2"}, + {"Կ", "կ", "к", "", "2"}, + {"Հ", "հ", "Հ", "", "3"}, + {"Ձ", "ձ", "дз", "", "1"}, + {"Ղ", "ղ", "гх", "", "3"}, + {"Ճ", "ճ", "тч", "", "2"}, + {"Մ", "մ", "м", "", "1"}, + {"Յ", "յ", "й", "", "2"}, + {"Ն", "ն", "н", "", "1"}, + {"Շ", "շ", "ш", "", "2"}, + {"Ո", "ո", "(в)о", "", "0"}, + {"Չ", "չ", "ч", "", "3"}, + {"Պ", "պ", "п", "", "2"}, + {"Ջ", "ջ", "дж", "", "1"}, + {"Ռ", "ռ", "рр", "", "1"}, + {"Ս", "ս", "с", "", "2"}, + {"Վ", "վ", "в", "", "1"}, + {"Տ", "տ", "т", "", "2"}, + {"Ր", "ր", "р", "", "1"}, + {"Ց", "ց", "ц", "", "3"}, + {"Ու", "ու", "у", "", "0"}, + {"Փ", "փ", "пх", "", "3"}, + {"Ք", "ք", "кх", "", "3"}, + {"Օ", "օ", "о", "", "0"}, + {"Ֆ", "ֆ", "ф", "", "2"}, + {"և", "և", "ев", "եւ", "0"}, +}; + + +Animation bgtex; +Font fontNew; +Font fontHand; +Font fontClassic; +Framebuffer fb; +int seed; + + +void drawLetter(const char **letter) { + const char *syms[4] = { letter[2], letter[0], letter[1], letter[3] }; + Font fonts[4] = { fontNew, fontNew, fontNew, fontHand }; + double size[4] = { 0.8, 1, 1, 1 }; + double offset[4] = { 0, 0.05, 0, 0 }; + if (!strcmp(syms[0], "Հ")) fonts[0] = fontClassic; + if (!syms[3][0]) syms[3] = syms[2]; + + unsigned int colors[4] = { COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_YELLOW }; + int ci = letter[4][0] - '0'; + if (ci < 0 || ci >= 4) ci = 2; + unsigned int color = colors[ci]; + + for(int r = 0, i = 0; r < 2; ++r) + for(int c = 0; c < 2; ++c, ++i) { + if (!syms[i][0]) continue; + saveState(); + translate(c*CW + CB, r*CH + CB); + + noStroke(); + double ts = 512/2; + fillTexture(bgtex, randomFloat()*ts, randomFloat()*ts, ts, ts, FALSE); + rectRounded(0, 0, CWW, CHH, CR); + + noFill(); + stroke(colorWithAlpha(COLOR_BLACK, 0.5)); + strokeWidth(1); + rectRounded(0.5, 0.5, CWW - 1, CHH - 1, CR); + + stroke(colorWithAlpha(color, 0.25)); + strokeWidth(SW); + rectRounded(0.5*SW, 0.5*SW, CWW - SW, CHH - SW, CR - 0.5*SW); + + stroke(colorWithAlpha(COLOR_BLACK, 0.75)); + textAlign(HALIGN_CENTER, VALIGN_CENTER); + textFont(fonts[i]); + textSize(FS*size[i]); + text(0.5*CWW, (0.5 + offset[i])*CHH, syms[i]); + + restoreState(); + } +} + + +void drawLetters() { + srand(seed); + for(int i = 0; i < sizeof(letters)/sizeof(*letters); ++i) { + saveState(); + translate(CW*2*(i%8), CH*2*(i/8)); + drawLetter(letters[i]); + restoreState(); + } +} + + +void save(const char *filename) { + saveState(); + resetState(); + target(fb); + background(COLOR_TRANSPARENT); + clear(); + drawLetters(); + if (viewportSave(filename)) + printf("saved to %s\n", filename); + restoreState(); +} + + +void init() { + fb = createFramebuffer(CW*2*8, CH*2*5); + bgtex = createAnimationEx("data/shisen/wood4.png", TRUE, TRUE, TRUE); + fontNew = createFont("data/shisen/arnamu.ttf"); + fontHand = createFont("data/shisen/Vrdznagir.otf"); + fontClassic = createFont("data/shisen/Arian_Grqi_i_U.ttf"); + seed = rand(); +} + + +void draw() { + saveState(); + //zoom(0.5); + drawLetters(); + if (keyWentDown("space")) seed = rand(); + if (keyWentDown("s")) save("data/shisen/letters-gen.png"); + restoreState(); +} + + +int main() { + windowSetResizable(TRUE); + windowSetVariableFrameRate(); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + + + diff --git a/onefile/shisen.c b/onefile/shisen.c new file mode 100644 index 0000000..0577638 --- /dev/null +++ b/onefile/shisen.c @@ -0,0 +1,384 @@ + +#include +#include +#include +#include +#include + +#include "helianthus.h" + +#define COLS 16 +#define ROWS 9 +#define MAXLETTERS 256 +#define RMTIME 1.0 + +#define COUNT (ROWS*COLS) + + +typedef struct { + Animation glyphs[4]; + int glyphsCount; + Animation g[4]; +} Letter; + + +typedef struct { + int r, c; +} Pos; + +typedef struct { + int len; + Pos p[4]; + double tm; +} Track; + +typedef struct { + Letter *l; + Animation g; + double tm; +} Cell; + + +Letter letters[MAXLETTERS]; +int lettersCount; +int cellWidth = 1; +int cellHeight = 1; + +Cell board[ROWS][COLS]; +Pos selected = { -1, -1 }; +Track tracks[COUNT]; +int tracksCount; +Animation bgtex; + +void clearBoard() { + memset(tracks, 0, sizeof(tracks)); + memset(board, 0, sizeof(board)); + tracksCount = 0; + selected = (Pos){ -1, -1 }; +} + +void clearLetters() { + clearBoard(); + for(int i = 0; i < lettersCount; ++i) + for(int j = 0; j < letters[i].glyphsCount; ++j) + animationDestroy(letters[i].glyphs[j]); + memset(letters, 0, sizeof(letters)); + lettersCount = 0; + cellWidth = cellHeight = 1; +} + +int loadLetters(const char *filename, int rows, int cols) { + clearLetters(); + + int w, h; + unsigned char *pixels; + if (!imageLoad(filename, &w, &h, &pixels)) + return FALSE; + int stride = w*4; + + int ww = w/cols/2; + int hh = h/rows/2; + if (!ww || !hh) + { free(pixels); return FALSE; } + + int bufstride = ww*4; + int bufsize = hh*bufstride; + unsigned char *buf = calloc(bufsize, 1); + + Letter *letter = letters; + for(int r = 0; r < rows; ++r) + for(int c = 0; c < cols; ++c) + { + for(int rr = 0; rr < 2; ++rr) + for(int cc = 0; cc < 2; ++cc) { + unsigned char *p = pixels + (r*2 + rr)*hh*stride + (c*2 + cc)*bufstride; + for(int rrr = 0; rrr < hh; ++rrr) memcpy(buf + rrr*bufstride, p + rrr*stride, bufstride); + unsigned int visible = 0; + for(unsigned char *b = buf + 3, *e = b + bufsize; b < e; b += 4) visible += *b; + if (!visible) continue; + letter->glyphs[ letter->glyphsCount++ ] = createAnimationFromImage(ww, hh, buf, TRUE); + } + if (letter->glyphsCount > 0) ++letter; + } + lettersCount = letter - letters; + cellWidth = ww; + cellHeight = hh; + + free(buf); + free(pixels); + return TRUE; +} + + +void shuffleLetters() { + for(int i = 0; i < lettersCount; ++i) { + int j = randomNumber(i, lettersCount - 1); + if (j != i) { Letter l = letters[i]; letters[i] = letters[j]; letters[j] = l; } + Letter *l = &letters[i]; + assert(l->glyphsCount > 0); + + for(int j = 1; j < l->glyphsCount; ++j) { + int k = randomNumber(j, l->glyphsCount - 1); + if (k != j) { Animation g = l->glyphs[j]; l->glyphs[j] = l->glyphs[k]; l->glyphs[k] = g; } + } + + if (l->glyphsCount <= 1) { + for(int j = 0; j < 4; ++j) + l->g[j] = l->glyphs[0]; + } else { + l->g[0] = l->glyphs[0]; + for(int j = 1; j < 4; ++j) + l->g[j] = l->glyphs[(j-1)%(l->glyphsCount-1) + 1]; + } + + for(int j = 0; j < 4; ++j) { + int k = randomNumber(j, 3); + if (k != j) { Animation g = l->g[j]; l->g[j] = l->g[k]; l->g[k] = g; } + assert(l->g[j]); + } + } +} + + +void generateBoard() { + clearBoard(); + shuffleLetters(); + int indices[COUNT]; + for(int i = 0; i < COUNT; ++i) indices[i] = i; + for(int i = 0; i < COUNT; ++i) { + int j = randomNumber(i, COUNT-1); + int id = indices[j]; indices[j] = indices[i]; indices[i] = id; + Letter *l = &letters[ (id/4)%lettersCount ]; + board[i/COLS][i%COLS].l = l; + board[i/COLS][i%COLS].g = l->g[id%4]; + } +} + + +Letter* get(Pos p) + { return p.r >= 0 && p.r < ROWS && p.c >= 0 && p.c < COLS ? board[p.r][p.c].l : NULL; } + + +int findSubTrack1(Track *t, Pos b) { + Pos p = t->p[t->len - 1]; + //printf(" -- -- ft1: %d %d -> %d %d\n", p.r, p.c, b.r, b.c); + if (p.c == b.c && p.r < b.r) { for(++p.r; p.r != b.r; ++p.r) if (get(p)) return FALSE; } else + if (p.c == b.c && p.r > b.r) { for(--p.r; p.r != b.r; --p.r) if (get(p)) return FALSE; } else + if (p.r == b.r && p.c < b.c) { for(++p.c; p.c != b.c; ++p.c) if (get(p)) return FALSE; } else + if (p.r == b.r && p.c > b.c) { for(--p.c; p.c != b.c; --p.c) if (get(p)) return FALSE; } else return FALSE; + t->p[t->len++] = p; + return TRUE; +} + + +int findSubTrack2(Track *t, Pos b, int horz) { + Pos *p = &t->p[t->len]; + *p = t->p[t->len - 1]; + + //printf(" -- ft2: %d %d -> %d %d %s\n", p->r, p->c, b.r, b.c, horz ? "horz" : "vert"); + if (p->c == b.c || p->r == b.r) return findSubTrack1(t, b); + + + ++t->len; + if (horz) { + if (p->c < b.c) { for(++p->c; p->c != b.c; ++p->c) if (get(*p)) break; } + else { for(--p->c; p->c != b.c; --p->c) if (get(*p)) break; } + } else { + if (p->r < b.r) { for(++p->r; p->r != b.r; ++p->r) if (get(*p)) break; } + else { for(--p->r; p->r != b.r; --p->r) if (get(*p)) break; } + } + if (!get(*p) && findSubTrack1(t, b)) return TRUE; + --t->len; + + return FALSE; +} + + +int findSubTrack3(Track *t, Pos b) { + Pos p0 = t->p[t->len - 1]; + Pos *p = &t->p[t->len]; + if (p->c == b.c && p->r == b.r) return FALSE; + + //printf("ft3: %d %d -> %d %d\n", p0.r, p0.c, b.r, b.c); + if (findSubTrack2(t, b, FALSE)) return TRUE; + + ++t->len; + + *p = p0; + for(++p->c; p->c <= COLS; ++p->c) + if (get(*p)) break; else + if (findSubTrack2(t, b, FALSE)) return TRUE; + + *p = p0; + for(--p->c; p->c >= -1; --p->c) + if (get(*p)) break; else + if (findSubTrack2(t, b, FALSE)) return TRUE; + + *p = p0; + for(++p->r; p->r <= ROWS; ++p->r) + if (get(*p)) break; else + if (findSubTrack2(t, b, TRUE)) return TRUE; + + *p = p0; + for(--p->r; p->r >= -1; --p->r) + if (get(*p)) break; else + if (findSubTrack2(t, b, TRUE)) return TRUE; + + --t->len; + return FALSE; +} + + +Track findTrack(Pos a, Pos b) { + Track t = {}; + t.p[t.len++] = a; + Letter *la = get(a); + Letter *lb = get(b); + return la && la == lb && (a.c != b.c || a.r != b.r) && findSubTrack3(&t, b) ? t : (Track){}; +} + + +void hint() { + if (tracksCount) return; + + for(int i = 0; i < COUNT; ++i) { + Pos a = { i/COLS, i%COLS }; + for(int j = 0; j < COUNT; ++j) { + Pos b = { j/COLS, j%COLS }; + Track t = findTrack(a, b); + if (t.len) { + t.tm = 1; + tracks[tracksCount++] = t; + } + } + } + + if (!tracksCount) return; // beep + + int i = randomNumber(0, tracksCount - 1); + if (i) tracks[0] = tracks[i]; + tracksCount = 1; +} + + +void init() { + bgtex = createAnimationEx("data/shisen/minas.png", TRUE, TRUE, TRUE); + if (!loadLetters("data/shisen/letters-gen.png", 5, 8)) { + printf("cannot load letters\n"); + windowStop(); + return; + } + generateBoard(); +} + + +void draw() { + double w = windowGetWidth(); + double h = windowGetHeight(); + double dt = windowGetFrameTime(); + + double kw = w/(cellWidth*(COLS + 2)); + double kh = h/(cellHeight*(ROWS + 2)); + double k = kw < kh ? kw: kh; + + saveState(); + translate(w*0.5, h*0.5); + + { + saveState(); + double aw = animationGetFrameOrigWidth(bgtex, 0); + double ah = animationGetFrameOrigHeight(bgtex, 0); + double kw = w/aw; + double kh = h/ah; + double k = kw > kh ? kw: kh; + aw *= k; + ah *= k; + + noStroke(); + rectTextured(bgtex, -0.5*aw, -0.5*ah, aw, ah); + fill(colorByRGBA(1.0, 1.0, 1.0, 0.5)); + rect(-0.5*aw, -0.5*ah, aw, ah); + restoreState(); + } + + + zoom(k); + translate(-0.5*COLS*cellWidth, -0.5*ROWS*cellHeight); + strokeWidth(0.1*cellWidth); + + double mx = mouseTransformedX(); + double my = mouseTransformedY(); + Pos hover = {}; + hover.c = (int)floor(mx/cellWidth); + hover.r = (int)floor(my/cellHeight); + if (mouseWentDown("left")) { + Track t = findTrack(selected, hover); + if (t.len) { + t.tm = 1; + Cell *ca = &board[selected.r][selected.c]; + Cell *cb = &board[hover.r][hover.c]; + ca->l = cb->l = NULL; + ca->tm = cb->tm = t.tm; + tracks[tracksCount++] = t; + } + selected = hover; + } + + if (keyWentDown("h")) hint(); + + // draw letters + for(int r = 0; r < ROWS; ++r) + for(int c = 0; c < COLS; ++c) { + Cell *cell = &board[r][c]; + if (!cell->g) continue; + + double a = 1; + if (cell->tm) { + cell->tm -= dt/RMTIME; + if (cell->tm <= 0) { + cell->g = NULL; + cell->tm = 0; + continue; + } + a = cell->tm; + } + + saveState(); + noStroke(); + fill(colorByRGBA(1.0, 1.0, 1.0, a)); + if (a == 1 && c == hover.c && r == hover.r) fill(colorByRGBA(0.9, 0.9, 0.9, a)); + if (a == 1 && c == selected.c && r == selected.r) fill(colorByRGBA(0.8, 0.8, 0.8, a)); + rectTextured(cell->g, c*cellWidth, r*cellHeight, cellWidth, cellHeight); + restoreState(); + } + + // draw tracks + int j = 0; + for(int i = 0; i < tracksCount; ++i) { + Track *t = &tracks[i]; + t->tm -= dt/RMTIME; + if (t->tm <= 0) { j = i + 1; continue; } + saveState(); + stroke(colorByRGBA(1, 0, 0, t->tm)); + for(int k = 0; k < t->len; ++k) + lineTo((t->p[k].c + 0.5)*cellWidth, (t->p[k].r + 0.5)*cellHeight); + strokePath(); + restoreState(); + } + tracksCount -= j; + memmove(tracks, tracks + j, sizeof(*tracks)*tracksCount); + + restoreState(); +} + + +int main() { + windowSetResizable(TRUE); + windowSetVariableFrameRate(); + windowSetInit(&init); + windowSetDraw(&draw); + windowRun(); +} + + +