Blame simple/x11/shisen/shisen.c

Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
#include <img.h>
Ivan Mahonin 7d8000
#include <snd.h>
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
#include <ctype.h>
Ivan Mahonin 7d8000
#include <dirent.h>
Ivan Mahonin 7d8000
5c4215
#define MAXSIZE       40
5c4215
#define MAXCOUNT (MAXSIZE*MAXSIZE)
5c4215
#define VBASE          8 // must be even
5c4215
#define HBASE         10 // must be even
Ivan Mahonin 7d8000
Ivan Mahonin c88cf2
#define LETTERSRATIO   7
Ivan Mahonin 7d8000
#define MAXLETTERS   256
Ivan Mahonin 7d8000
#define MAXSOUNDS     16
Ivan Mahonin 7d8000
#define RMTIME       1.0
Ivan Mahonin 7d8000
#define EXTIME       2.0
Ivan Mahonin 7d8000
#define SNDRATE    44100
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
typedef struct {
Ivan Mahonin 7d8000
  Image src;
Ivan Mahonin 7d8000
  XImage *x;
Ivan Mahonin 7d8000
} ImageEx;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
typedef struct {
Ivan Mahonin de1fb3
  ImageEx a, b;
Ivan Mahonin de1fb3
} ImagePair;
Ivan Mahonin de1fb3
Ivan Mahonin de1fb3
typedef struct {
Ivan Mahonin de1fb3
  ImagePair glyphs[4];
Ivan Mahonin 7d8000
  int glyphsCount;
Ivan Mahonin 7d8000
  Sound sounds[MAXSOUNDS];
Ivan Mahonin 7d8000
  int soundsCount;
Ivan Mahonin de1fb3
  ImagePair *g[4];
Ivan Mahonin 7d8000
} Letter;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
typedef struct {
Ivan Mahonin 7d8000
  int r, c;
Ivan Mahonin 7d8000
} Pos;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
typedef struct {
Ivan Mahonin 7d8000
  int len;
Ivan Mahonin 7d8000
  Pos p[4];
Ivan Mahonin 7d8000
} Track;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
typedef struct {
Ivan Mahonin 7d8000
  Letter *l;
Ivan Mahonin de1fb3
  ImagePair *g;
Ivan Mahonin 7d8000
} Cell;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
5c4215
int cols = 4;
5c4215
int rows = 4;
5c4215
int count = 4*4;
5c4215
Ivan Mahonin 7d8000
Letter menuLetter;
Ivan Mahonin 7d8000
Letter letters[MAXLETTERS];
Ivan Mahonin 7d8000
int lettersCount;
5c4215
int baseCellWidth = 1;
5c4215
int baseCellHeight = 1;
Ivan Mahonin 7d8000
int cellWidth = 1;
Ivan Mahonin 7d8000
int cellHeight = 1;
Ivan Mahonin 7d8000
5c4215
Cell board[MAXSIZE][MAXSIZE];
Ivan Mahonin 7d8000
Pos hover = { -1, -1 };
Ivan Mahonin 7d8000
Pos selected = { -1, -1 };
5c4215
Track tracks[MAXCOUNT];
Ivan Mahonin 7d8000
int tracksCount;
Ivan Mahonin 7d8000
int glyphsMask = 0;
Ivan Mahonin 7d8000
int mode;
Ivan Mahonin 86bfaf
Letter *hintLetter;
Ivan Mahonin 7d8000
int back;
Ivan Mahonin de1fb3
int mute;
Ivan Mahonin 7d8000
int ox, oy, cw, ch;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
ImageEx bgtex;
Ivan Mahonin 7d8000
ImageEx starttex;
Ivan Mahonin 7d8000
ImageEx hinttex;
Ivan Mahonin 7d8000
ImageEx backtex[3];
Ivan Mahonin de1fb3
ImageEx sndtex[3];
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void imgExFree(ImageEx *img) {
Ivan Mahonin de1fb3
  if (!img) return;
Ivan Mahonin 7d8000
  if (img->x) XDestroyImage(img->x);
Ivan Mahonin 7d8000
  imgFree(&img->src);
Ivan Mahonin 7d8000
  *img = (ImageEx){};
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin de1fb3
void imgExResample(ImageEx *img, int w, int h) {
Ivan Mahonin 7d8000
  if (!imgValid(img->src) || w <= 0 || h <= 0) return;
Ivan Mahonin 7d8000
  if (img->x && (img->x->width != w || img->x->height != h))
Ivan Mahonin 7d8000
    { XDestroyImage(img->x); img->x = NULL; }
Ivan Mahonin 7d8000
  if (!img->x) {
Ivan Mahonin 7d8000
    Image tmp = imgResample(img->src, w, h);
Ivan Mahonin 7d8000
    imgDivA(tmp);
Ivan Mahonin 7d8000
    img->x = imgToX(&tmp);
Ivan Mahonin 7d8000
  }
Ivan Mahonin de1fb3
}
Ivan Mahonin de1fb3
Ivan Mahonin de1fb3
Ivan Mahonin de1fb3
void imgExDraw(ImageEx *img, int x, int y, int w, int h) {
Ivan Mahonin de1fb3
  imgExResample(img, w, h);
Ivan Mahonin 7d8000
  if (img->x)
Ivan Mahonin 7d8000
    XPutImage(dpy, drw, gc, img->x, 0, 0, x, y, img->x->width, img->x->height);
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
int imgExLoadTga(ImageEx *img, const char *filename) {
Ivan Mahonin 7d8000
  imgExFree(img);
Ivan Mahonin 7d8000
  img->src = imgLoadTga(filename);
Ivan Mahonin 7d8000
  imgMultA(img->src);
Ivan Mahonin 7d8000
  return imgValid(img->src);
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin de1fb3
void imgPairFree(ImagePair *img) {
Ivan Mahonin de1fb3
  if (!img) return;
Ivan Mahonin de1fb3
  imgExFree(&img->a);
Ivan Mahonin de1fb3
  imgExFree(&img->b);
Ivan Mahonin de1fb3
}
Ivan Mahonin de1fb3
Ivan Mahonin de1fb3
Ivan Mahonin de1fb3
void imgPairDraw(ImagePair *img, int mode, int x, int y, int w, int h) {
Ivan Mahonin de1fb3
  imgExResample(mode ? &img->a : &img->b, w, h);
Ivan Mahonin de1fb3
  imgExDraw(mode ? &img->b : &img->a, x, y, w, h);
Ivan Mahonin de1fb3
}
Ivan Mahonin de1fb3
Ivan Mahonin de1fb3
Ivan Mahonin 7d8000
void clearBoard() {
Ivan Mahonin de1fb3
  if (mute != 1) mute = 0;
Ivan Mahonin 7d8000
  mode = 0;
Ivan Mahonin 7d8000
  back = 0;
Ivan Mahonin 86bfaf
  hintLetter = NULL;
Ivan Mahonin 7d8000
  memset(tracks, 0, sizeof(tracks));
Ivan Mahonin 7d8000
  memset(board, 0, sizeof(board));
Ivan Mahonin 7d8000
  tracksCount = 0;
Ivan Mahonin 7d8000
  selected = (Pos){ -1, -1 };
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void clearSounds() {
Ivan Mahonin 7d8000
  for(int i = 0; i < lettersCount; ++i)
Ivan Mahonin 7d8000
    for(int j = 0; j < letters[i].soundsCount; ++i)
Ivan Mahonin 7d8000
      sndFree(&letters[i].sounds[j]);
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void clearLetters() {
Ivan Mahonin 7d8000
  clearBoard();
Ivan Mahonin 7d8000
  clearSounds();
Ivan Mahonin 7d8000
  for(int i = 0; i < lettersCount; ++i)
Ivan Mahonin 7d8000
    for(int j = 0; j < letters[i].glyphsCount; ++j)
Ivan Mahonin de1fb3
      imgPairFree(&letters[i].glyphs[j]);
Ivan Mahonin 7d8000
  memset(letters, 0, sizeof(letters));
Ivan Mahonin 7d8000
  memset(&menuLetter, 0, sizeof(menuLetter));
Ivan Mahonin 7d8000
  lettersCount = 0;
Ivan Mahonin 7d8000
  cellWidth = cellHeight = 1;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin de1fb3
int loadLetters(const char *filename, const char *filesel, int rows, int cols) {
Ivan Mahonin 7d8000
  clearLetters();
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  Image img = imgLoadTga(filename);
Ivan Mahonin 7d8000
  if (!imgValid(img))
Ivan Mahonin 7d8000
    return 0;
Ivan Mahonin 7d8000
  int ww = img.w/cols/2;
Ivan Mahonin 7d8000
  int hh = img.h/rows/2;
Ivan Mahonin 7d8000
  if (!ww || !hh)
Ivan Mahonin 7d8000
    return LOGERR("loadLetters: image too small (%dx%d): %s", img.w, img.h, filename), imgFree(&img), 0;
Ivan Mahonin 7d8000
Ivan Mahonin de1fb3
  Image imgsel = imgLoadTga(filesel);
Ivan Mahonin de1fb3
  if (!imgValid(imgsel))
Ivan Mahonin de1fb3
    return imgFree(&img), 0;
Ivan Mahonin de1fb3
  int wws = imgsel.w/cols/2;
Ivan Mahonin de1fb3
  int hhs = imgsel.h/rows/2;
Ivan Mahonin de1fb3
  if (!wws || !hhs)
Ivan Mahonin de1fb3
    return LOGERR("loadLetters: image too small (%dx%d): %s", imgsel.w, imgsel.h, filesel), imgFree(&img), imgFree(&imgsel), 0;
Ivan Mahonin de1fb3
Ivan Mahonin 7d8000
  imgMultA(img);
Ivan Mahonin 7d8000
  Letter *letter = letters;
Ivan Mahonin 7d8000
  for(int r = 0; r < rows; ++r)
Ivan Mahonin 7d8000
  for(int c = 0; c < cols; ++c)
Ivan Mahonin 7d8000
  {
Ivan Mahonin 7d8000
    for(int rr = 0; rr < 2; ++rr)
Ivan Mahonin 7d8000
    for(int cc = 0; cc < 2; ++cc) {
Ivan Mahonin 7d8000
      Image sub = imgSub(img, (c*2 + cc)*ww, (r*2 + rr)*hh, ww, hh);
Ivan Mahonin 7d8000
      if (!imgHasPicture(sub))
Ivan Mahonin 7d8000
        { imgFree(&sub); continue; }
Ivan Mahonin de1fb3
      letter->glyphs[ letter->glyphsCount ].a.src = sub;
Ivan Mahonin de1fb3
      letter->glyphs[ letter->glyphsCount ].b.src = imgSub(imgsel, (c*2 + cc)*wws, (r*2 + rr)*hhs, wws, hhs);
Ivan Mahonin de1fb3
      ++letter->glyphsCount;
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
    if (letter->glyphsCount > menuLetter.glyphsCount) menuLetter = *letter;
Ivan Mahonin 7d8000
    if (letter->glyphsCount > 0) ++letter;
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
  lettersCount = letter - letters;
5c4215
  baseCellWidth = wws;
5c4215
  baseCellHeight = hhs;
Ivan Mahonin 7d8000
  cellWidth = ww;
Ivan Mahonin 7d8000
  cellHeight = hh;
Ivan Mahonin 7d8000
  assert(menuLetter.glyphsCount == 4);
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  imgFree(&img);
Ivan Mahonin 7d8000
  return 1;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void loadSounds(const char *path) {
Ivan Mahonin 7d8000
  DIR *dir = opendir(path);
Ivan Mahonin 7d8000
  if (!dir) return;
Ivan Mahonin 7d8000
  size_t pl = strlen(path);
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  char buf[1024];
Ivan Mahonin 7d8000
  while(1) {
Ivan Mahonin 7d8000
    struct dirent *e = readdir(dir);
Ivan Mahonin 7d8000
    if (!e) break;
Ivan Mahonin 7d8000
    size_t nl = strlen(e->d_name);
Ivan Mahonin 7d8000
    if (nl < 4 || pl + nl + 1 >= sizeof(buf)) continue;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    const char *ex = e->d_name + nl - 4;
Ivan Mahonin 7d8000
    if ( ex[0] != '.'
Ivan Mahonin 7d8000
     || tolower(ex[1]) != 'r'
Ivan Mahonin 7d8000
     || tolower(ex[2]) != 'a'
Ivan Mahonin 7d8000
     || tolower(ex[3]) != 'w' ) continue;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    int index = atoi(e->d_name);
Ivan Mahonin 7d8000
    if (index <= 0 || index > lettersCount) continue;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    Letter *l = &letters[index-1];
Ivan Mahonin 7d8000
    if (l->soundsCount >= MAXSOUNDS) continue;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    sprintf(buf, "%s/%s", path, e->d_name);
Ivan Mahonin 7d8000
    l->sounds[l->soundsCount] = sndLoadRaw(buf, SNDRATE);
Ivan Mahonin 7d8000
    //l->sounds[l->soundsCount] = sndGen(SNDRATE, 100, SNDRATE);
Ivan Mahonin 7d8000
    if (sndValid(l->sounds[l->soundsCount])) ++l->soundsCount;
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
  closedir(dir);
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void shuffleLetters(int glyps) {
Ivan Mahonin 7d8000
  for(int i = 0; i < lettersCount; ++i) {
Ivan Mahonin 7d8000
    int j = rand()%(lettersCount - i) + i;
Ivan Mahonin 7d8000
    if (j != i) { Letter l = letters[i]; letters[i] = letters[j]; letters[j] = l; }
Ivan Mahonin 7d8000
    Letter *l = &letters[i];
Ivan Mahonin 7d8000
    assert(l->glyphsCount > 0);
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    int gc = 0;
Ivan Mahonin de1fb3
    ImagePair *g[4] = {};
Ivan Mahonin 7d8000
    for(int j = 0; j < l->glyphsCount; ++j)
Ivan Mahonin 7d8000
      if (glyps & (1 << j)) g[gc++] = &l->glyphs[j];
Ivan Mahonin 7d8000
    if (!gc) g[gc++] = &l->glyphs[0];
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    for(int j = 0; j < gc; ++j) {
Ivan Mahonin 7d8000
      int k = rand()%(gc - j) + j;
Ivan Mahonin de1fb3
      if (k != j) { ImagePair *a = g[j]; g[j] = g[k]; g[k] = a; }
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    for(int j = 0; j < 4; ++j)
Ivan Mahonin 7d8000
      l->g[j] = g[j%gc];
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    for(int j = 0; j < 4; ++j) {
Ivan Mahonin 7d8000
      int k = rand()%(4 - j) + j;
Ivan Mahonin de1fb3
      if (k != j) { ImagePair *g = l->g[j]; l->g[j] = l->g[k]; l->g[k] = g; }
Ivan Mahonin 7d8000
      assert(l->g[j]);
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
5c4215
void calcSize() {
5c4215
    if (winW*baseCellHeight < baseCellWidth*winH) {
5c4215
        cols = HBASE; // portrait
5c4215
        rows = winH*cols*baseCellWidth/(winW*baseCellHeight);
5c4215
    } else {
5c4215
        rows = VBASE; // landscape
5c4215
        cols = winW*rows*baseCellHeight/(winH*baseCellWidth);
5c4215
    }
5c4215
    cols -= 4; // place for buttons
5c4215
    rows -= 2;
5c4215
    if (rows < 1) rows = 1;
5c4215
    if (cols < 1) cols = 1;
5c4215
    if (rows > MAXSIZE) rows = MAXSIZE;
5c4215
    if (cols > MAXSIZE) cols = MAXSIZE;
5c4215
    count = rows*cols;
5c4215
}
5c4215
5c4215
Ivan Mahonin 7d8000
void simpleBoard() {
Ivan Mahonin 7d8000
  clearBoard();
5c4215
  calcSize();
5c4215
  for(int i = 0; i < count; ++i) {
Ivan Mahonin 7d8000
    Letter *l = &letters[i%lettersCount];
5c4215
    board[i/cols][i%cols].l = l;
5c4215
    board[i/cols][i%cols].g = &l->glyphs[1];
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
  mode = 1;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin c88cf2
void generateBoard(int glyphsMask) {
Ivan Mahonin 7d8000
  clearBoard();
5c4215
  calcSize();
Ivan Mahonin 7d8000
  shuffleLetters(glyphsMask);
Ivan Mahonin 7d8000
5c4215
  int indices[MAXCOUNT];
5c4215
  for(int i = 0; i < count; ++i) indices[i] = i;
Ivan Mahonin 7d8000
Ivan Mahonin c88cf2
  int maxLetters = count/LETTERSRATIO + 1;
Ivan Mahonin 7d8000
  int lc = lettersCount < maxLetters ? lettersCount : maxLetters;
5c4215
  for(int i = 0; i < count; ++i) {
5c4215
    int j = rand()%(count - i) + i;
Ivan Mahonin 7d8000
    int id = indices[j]; indices[j] = indices[i]; indices[i] = id;
Ivan Mahonin 7d8000
    Letter *l = &letters[ (id/4)%lc ];
5c4215
    board[i/cols][i%cols].l = l;
5c4215
    board[i/cols][i%cols].g = l->g[id%4];
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  mode = 1;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Letter* get(Pos p)
5c4215
  { return p.r >= 0 && p.r < rows && p.c >= 0 && p.c < cols ? board[p.r][p.c].l : NULL; }
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
int findSubTrack1(Track *t, Pos b) {
Ivan Mahonin 7d8000
  Pos p = t->p[t->len - 1];
Ivan Mahonin 7d8000
  //printf(" --  -- ft1: %d %d -> %d %d\n", p.r, p.c, b.r, b.c);
Ivan Mahonin 7d8000
  if (p.c == b.c && p.r < b.r) { for(++p.r; p.r != b.r; ++p.r) if (get(p)) return 0; } else
Ivan Mahonin 7d8000
  if (p.c == b.c && p.r > b.r) { for(--p.r; p.r != b.r; --p.r) if (get(p)) return 0; } else
Ivan Mahonin 7d8000
  if (p.r == b.r && p.c < b.c) { for(++p.c; p.c != b.c; ++p.c) if (get(p)) return 0; } else
Ivan Mahonin 7d8000
  if (p.r == b.r && p.c > b.c) { for(--p.c; p.c != b.c; --p.c) if (get(p)) return 0; } else return 0;
Ivan Mahonin 7d8000
  t->p[t->len++] = p;
Ivan Mahonin 7d8000
  return 1;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
int findSubTrack2(Track *t, Pos b, int horz) {
Ivan Mahonin 7d8000
  Pos *p = &t->p[t->len];
Ivan Mahonin 7d8000
  *p = t->p[t->len - 1];
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  //printf(" -- ft2: %d %d -> %d %d %s\n", p->r, p->c, b.r, b.c, horz ? "horz" : "vert");
Ivan Mahonin 7d8000
  if (p->c == b.c || p->r == b.r) return findSubTrack1(t, b);
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  ++t->len;
Ivan Mahonin 7d8000
  if (horz) {
Ivan Mahonin 7d8000
    if (p->c < b.c) { for(++p->c; p->c != b.c; ++p->c) if (get(*p)) break; }
Ivan Mahonin 7d8000
               else { for(--p->c; p->c != b.c; --p->c) if (get(*p)) break; }
Ivan Mahonin 7d8000
  } else {
Ivan Mahonin 7d8000
    if (p->r < b.r) { for(++p->r; p->r != b.r; ++p->r) if (get(*p)) break; }
Ivan Mahonin 7d8000
               else { for(--p->r; p->r != b.r; --p->r) if (get(*p)) break; }
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
  if (!get(*p) && findSubTrack1(t, b)) return 1;
Ivan Mahonin 7d8000
  --t->len;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  return 0;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
int findSubTrack3(Track *t, Pos b) {
Ivan Mahonin 7d8000
  Pos p0 = t->p[t->len - 1];
Ivan Mahonin 7d8000
  Pos *p = &t->p[t->len];
Ivan Mahonin 7d8000
  if (p->c == b.c && p->r == b.r) return 0;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  //printf("ft3: %d %d -> %d %d\n", p0.r, p0.c, b.r, b.c);
Ivan Mahonin 7d8000
  if (findSubTrack2(t, b, 0)) return 1;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  ++t->len;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  *p = p0;
5c4215
  for(++p->c; p->c <= cols; ++p->c)
Ivan Mahonin 7d8000
    if (get(*p)) break; else
Ivan Mahonin 7d8000
      if (findSubTrack2(t, b, 0)) return 1;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  *p = p0;
Ivan Mahonin 7d8000
  for(--p->c; p->c >= -1; --p->c)
Ivan Mahonin 7d8000
    if (get(*p)) break; else
Ivan Mahonin 7d8000
      if (findSubTrack2(t, b, 0)) return 1;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  *p = p0;
5c4215
  for(++p->r; p->r <= rows; ++p->r)
Ivan Mahonin 7d8000
    if (get(*p)) break; else
Ivan Mahonin 7d8000
      if (findSubTrack2(t, b, 1)) return 1;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  *p = p0;
Ivan Mahonin 7d8000
  for(--p->r; p->r >= -1; --p->r)
Ivan Mahonin 7d8000
    if (get(*p)) break; else
Ivan Mahonin 7d8000
      if (findSubTrack2(t, b, 1)) return 1;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  --t->len;
Ivan Mahonin 7d8000
  return 0;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Track findTrack(Pos a, Pos b) {
Ivan Mahonin 7d8000
  Track t = {};
Ivan Mahonin 7d8000
  t.p[t.len++] = a;
Ivan Mahonin 7d8000
  Letter *la = get(a);
Ivan Mahonin 7d8000
  Letter *lb = get(b);
Ivan Mahonin 7d8000
  return la && la == lb && (a.c != b.c || a.r != b.r) && findSubTrack3(&t, b) ? t : (Track){};
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void hint() {
Ivan Mahonin 7d8000
  if (tracksCount) return;
Ivan Mahonin 7d8000
5c4215
  for(int i = 0; i < count; ++i) {
5c4215
    Pos a = { i/cols, i%cols };
5c4215
    for(int j = 0; j < count; ++j) {
5c4215
      Pos b = { j/cols, j%cols };
Ivan Mahonin 7d8000
      Track t = findTrack(a, b);
Ivan Mahonin 7d8000
      if (t.len)
Ivan Mahonin 7d8000
        tracks[tracksCount++] = t;
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  LOGDBG("hint: tracks count: %d", tracksCount);
Ivan Mahonin 7d8000
  if (!tracksCount) return; // beep
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  int i = rand()%tracksCount;
Ivan Mahonin 7d8000
  if (i) tracks[0] = tracks[i];
Ivan Mahonin 7d8000
  tracksCount = 1;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
int init() {
Ivan Mahonin 7d8000
  sndInit();
Ivan Mahonin 7d8000
  srand(time(NULL));
Ivan Mahonin 7d8000
  imgExLoadTga(&bgtex,      "data/minas.tga");
Ivan Mahonin 7d8000
  imgExLoadTga(&starttex,   "data/start.tga");
Ivan Mahonin 7d8000
  imgExLoadTga(&hinttex,    "data/hint.tga");
Ivan Mahonin 7d8000
  imgExLoadTga(&backtex[0], "data/back.tga");
Ivan Mahonin 7d8000
  imgExLoadTga(&backtex[1], "data/back1.tga");
Ivan Mahonin 7d8000
  imgExLoadTga(&backtex[2], "data/back2.tga");
Ivan Mahonin de1fb3
  imgExLoadTga(&sndtex[0],  "data/sound.tga");
Ivan Mahonin de1fb3
  imgExLoadTga(&sndtex[1],  "data/silence.tga");
Ivan Mahonin de1fb3
  imgExLoadTga(&sndtex[2],  "data/sound2.tga");
Ivan Mahonin de1fb3
  if (!loadLetters("data/letters.tga", "data/letters-sel.tga", 5, 8))
Ivan Mahonin 7d8000
    return LOGERR("init: cannot load letters"), 0;
Ivan Mahonin 7d8000
  loadSounds("data/sounds");
Ivan Mahonin 7d8000
  //simpleBoard();
Ivan Mahonin 7d8000
  return 1;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void deinit() {
Ivan Mahonin 7d8000
  clearLetters();
Ivan Mahonin 7d8000
  imgExFree(&bgtex);
Ivan Mahonin 7d8000
  imgExFree(&starttex);
Ivan Mahonin 7d8000
  imgExFree(&hinttex);
Ivan Mahonin 7d8000
  imgExFree(&backtex[0]);
Ivan Mahonin 7d8000
  imgExFree(&backtex[1]);
Ivan Mahonin 7d8000
  imgExFree(&backtex[2]);
Ivan Mahonin de1fb3
  imgExFree(&sndtex[0]);
Ivan Mahonin de1fb3
  imgExFree(&sndtex[1]);
Ivan Mahonin de1fb3
  imgExFree(&sndtex[2]);
Ivan Mahonin 7d8000
  sndDeinit();
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void resize() { }
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void mouseDown(int x, int y) {
Ivan Mahonin 7d8000
  int mc = (x - ox)/cw;
Ivan Mahonin 7d8000
  int mr = (y - oy)/ch;
Ivan Mahonin 7d8000
  if (x < ox) --mc;
Ivan Mahonin 7d8000
  if (y < oy) --mr;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  if (mode == 0) {
Ivan Mahonin 7d8000
    if (!mr && mc >= 0 && mc < 4) {
Ivan Mahonin 7d8000
      glyphsMask ^= 1 << mc;
Ivan Mahonin 7d8000
    } else
Ivan Mahonin 7d8000
    if (mr == 1 && mc >= 1 && mc <= 2) {
Ivan Mahonin c88cf2
      generateBoard(glyphsMask);
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
  } else {
Ivan Mahonin 7d8000
    if (mc < 0 && mr < 0) {
Ivan Mahonin 7d8000
      if (++back >= 3) clearBoard();
Ivan Mahonin 7d8000
      return;
Ivan Mahonin 7d8000
    } else back = 0;
Ivan Mahonin 7d8000
Ivan Mahonin 86bfaf
    hover.r = hover.c = -1;
Ivan Mahonin 86bfaf
    hintLetter = NULL;
Ivan Mahonin 86bfaf
    tracksCount = 0;
5c4215
    for(int r = 0; r < rows; ++r)
5c4215
    for(int c = 0; c < cols; ++c)
Ivan Mahonin 86bfaf
      if (!board[r][c].l) board[r][c].g = NULL;
Ivan Mahonin 86bfaf
5c4215
    if (mc >= cols && mr >= rows) {
Ivan Mahonin 86bfaf
      hintLetter = get(selected);
Ivan Mahonin 86bfaf
      if (!hintLetter) hint();
Ivan Mahonin 86bfaf
      selected.r = selected.c = -1;
Ivan Mahonin 7d8000
    } else
5c4215
    if (mc >= cols && mr < 0) {
Ivan Mahonin de1fb3
      mute = mute == 1 ? 2 : 1;
Ivan Mahonin de1fb3
    } else
5c4215
    if (mc >= 0 && mr >= 0 && mc < cols && mr < rows) {
Ivan Mahonin 7d8000
      hover.r = mr;
Ivan Mahonin 7d8000
      hover.c = mc;
Ivan Mahonin 7d8000
      Letter *l = get(hover);
Ivan Mahonin de1fb3
      if (l && l->soundsCount > 0 && mute != 1)
Ivan Mahonin 7d8000
        sndPlay(l->sounds[rand() % l->soundsCount]);
Ivan Mahonin 7d8000
      if (!l || (mr == selected.r && mc == selected.c)) {
Ivan Mahonin 7d8000
        selected.r = selected.c = hover.r = hover.c = -1;
Ivan Mahonin 7d8000
      } else
Ivan Mahonin 7d8000
      if (l == get(selected)) {
Ivan Mahonin 7d8000
        Track t = findTrack(selected, hover);
Ivan Mahonin 7d8000
        if (t.len) {
Ivan Mahonin 7d8000
          Cell *ca = &board[selected.r][selected.c];
Ivan Mahonin 7d8000
          Cell *cb = &board[hover.r][hover.c];
Ivan Mahonin 7d8000
          ca->l = cb->l = NULL;
Ivan Mahonin 7d8000
          tracks[tracksCount++] = t;
Ivan Mahonin 7d8000
        } else {
Ivan Mahonin 7d8000
          selected = hover;
Ivan Mahonin 7d8000
        }
Ivan Mahonin 7d8000
      } else {
Ivan Mahonin 7d8000
        selected = hover;
Ivan Mahonin 7d8000
      }
Ivan Mahonin 7d8000
    } else {
Ivan Mahonin 7d8000
      selected.r = selected.c = hover.r = hover.c = -1;
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void mouseUp() {
Ivan Mahonin 86bfaf
  int cnt = 0;
5c4215
  for(int r = 0; r < rows; ++r)
5c4215
  for(int c = 0; c < cols; ++c)
Ivan Mahonin 86bfaf
    if (!board[r][c].l && board[r][c].g)
Ivan Mahonin 86bfaf
      { ++cnt; board[r][c].g = NULL; }
Ivan Mahonin 86bfaf
  if (cnt) tracksCount = 0;
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
void draw() {
Ivan Mahonin 7d8000
  if (imgValid(bgtex.src)) {
Ivan Mahonin 7d8000
    if (winW*bgtex.src.h > winH*bgtex.src.w) {
Ivan Mahonin 7d8000
      int h = bgtex.src.h*winW/bgtex.src.w;
Ivan Mahonin 7d8000
      imgExDraw(&bgtex, 0, (winH - h)/2, winW, h);
Ivan Mahonin 7d8000
    } else {
Ivan Mahonin 7d8000
      int w = bgtex.src.w*winH/bgtex.src.h;
Ivan Mahonin 7d8000
      imgExDraw(&bgtex, (winW - w)/2, 0, w, winH);
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
  if (mode == 0) {
Ivan Mahonin 7d8000
    if (winW*cellHeight*3 < winH*cellWidth*6) {
Ivan Mahonin 7d8000
      cw = winW/6;
Ivan Mahonin 7d8000
      ch = cellHeight*cw/cellWidth;
Ivan Mahonin 7d8000
    } else {
Ivan Mahonin 7d8000
      ch = winH/3;
Ivan Mahonin 7d8000
      cw = cellWidth*ch/cellHeight;
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
    ox = (winW - cw*4)/2;
Ivan Mahonin 7d8000
    oy = (winH - ch)/2;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    for(int i = 0; i < 4; ++i)
Ivan Mahonin de1fb3
      imgPairDraw(&menuLetter.glyphs[i], !(glyphsMask & (1 << i)), ox + i*cw, oy, cw, ch);
Ivan Mahonin 7d8000
    if (glyphsMask)
Ivan Mahonin 7d8000
      imgExDraw(&starttex, ox + cw, oy + ch, 2*cw, ch);
Ivan Mahonin 7d8000
  } else {
5c4215
    if (winW*cellHeight*(rows + 2) < winH*cellWidth*(cols + 4)) {
5c4215
      cw = winW/(cols + 4);
Ivan Mahonin 7d8000
      ch = cellHeight*cw/cellWidth;
Ivan Mahonin 7d8000
    } else {
5c4215
      ch = winH/(rows + 2);
Ivan Mahonin 7d8000
      cw = cellWidth*ch/cellHeight;
Ivan Mahonin 7d8000
    }
5c4215
    ox = (winW - cw*cols)/2;
5c4215
    oy = (winH - ch*rows)/2;
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    // draw buttons
Ivan Mahonin 7d8000
    imgExDraw(&backtex[back], ox - 2*cw, oy - ch, 2*cw, ch);
5c4215
    imgExDraw(&hinttex, ox + cols*cw, oy + rows*ch, 2*cw, ch);
5c4215
    imgExDraw(&sndtex[mute], ox + cols*cw, oy - ch, 2*cw, ch);
Ivan Mahonin de1fb3
    for(int i = 0; i < 3; ++i) imgExResample(&sndtex[i], cw, ch);
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    // draw cells
5c4215
    for(int r = 0; r < rows; ++r)
5c4215
    for(int c = 0; c < cols; ++c) {
Ivan Mahonin 7d8000
      Cell *cell = &board[r][c];
Ivan Mahonin 7d8000
      if (!cell->g) continue;
Ivan Mahonin de1fb3
      int hilight = (r == hover.r && c == hover.c)
Ivan Mahonin de1fb3
                 || (r == selected.r && c == selected.c)
Ivan Mahonin 86bfaf
                 || (hintLetter && hintLetter == cell->l);
Ivan Mahonin de1fb3
      imgPairDraw(cell->g, hilight, ox + c*cw, oy + r*ch, cw, ch);
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
Ivan Mahonin 7d8000
    // draw tracks
Ivan Mahonin de1fb3
    int lw = cw/6;
Ivan Mahonin de1fb3
    if (lw < 1) lw = 1;
Ivan Mahonin 7d8000
    XSetForeground(dpy, gc, 0xff0000);
Ivan Mahonin de1fb3
    XSetLineAttributes(dpy, gc, lw, LineSolid, CapRound, JoinRound);
Ivan Mahonin 7d8000
    for(int i = 0; i < tracksCount; ++i) {
Ivan Mahonin 7d8000
      Track *t = &tracks[i];
Ivan Mahonin 7d8000
      XPoint p[4];
Ivan Mahonin 7d8000
      for(int j = 0; j < t->len; ++j) {
Ivan Mahonin 7d8000
        p[j].x = ox + t->p[j].c*cw + cw/2;
Ivan Mahonin 7d8000
        p[j].y = oy + t->p[j].r*ch + ch/2;
Ivan Mahonin 7d8000
      }
Ivan Mahonin 7d8000
      XDrawLines(dpy, drw, gc, p, t->len, CoordModeOrigin);
Ivan Mahonin 7d8000
    }
Ivan Mahonin 7d8000
  }
Ivan Mahonin 7d8000
}
Ivan Mahonin 7d8000