Blame projects/jigsaw/game.c

Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
#include "game.h"
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
void gameFree(Game *gm) {
Ivan Mahonin fdbd7d
  puzzleFree(&gm->pz);
Ivan Mahonin fdbd7d
  if (gm->pz.image) animationDestroy(gm->pz.image);
Ivan Mahonin fdbd7d
  free(gm->path);
Ivan Mahonin fdbd7d
  free(gm->imgname);
Ivan Mahonin fdbd7d
  memset(gm, 0, sizeof(*gm));
Ivan Mahonin fdbd7d
}
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
int gameCreate(Game *gm, const char *path, const char *imgname, int rows, int cols, int turn, int seed) {
Ivan Mahonin fdbd7d
  gameFree(gm);
Ivan Mahonin fdbd7d
  char* imgpath = findImage(imgname);
Ivan Mahonin fdbd7d
  if (!imgpath) return ERR("gameCreate: cannot find image: %s", imgname);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  gm->pz.image = createAnimationEx(imgpath, TRUE, FALSE, FALSE);
Ivan Mahonin fdbd7d
  free(imgpath);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  int iw = animationGetOrigWidth(gm->pz.image);
Ivan Mahonin fdbd7d
  int ih = animationGetOrigHeight(gm->pz.image);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  if (rows < 1 || cols < 1 || rows*cols < 2) {
Ivan Mahonin fdbd7d
    int count = rows;
Ivan Mahonin fdbd7d
    if (count < 4) count = 4;
Ivan Mahonin fdbd7d
    double s = sqrt(iw*ih/(double)count);
Ivan Mahonin fdbd7d
    cols = (int)round(iw/s);
Ivan Mahonin fdbd7d
    if (cols < 1) cols = 1;
Ivan Mahonin fdbd7d
    rows = (int)round(ih/s);
Ivan Mahonin fdbd7d
    if (rows < 1) rows = 1;
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  double cw = iw/(double)cols;
Ivan Mahonin fdbd7d
  double ch = ih/(double)rows;
Ivan Mahonin fdbd7d
  if (cw > ch) { ch /= cw; cw = 1; }
Ivan Mahonin fdbd7d
          else { cw /= ch; ch = 1; }
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  int px = (int)exp2(round(log2(1024/cols)));
Ivan Mahonin fdbd7d
  if (px < 8) px = 8;
Ivan Mahonin fdbd7d
  if (px > 1024) px = 1024;
Ivan Mahonin fdbd7d
  DBG("iw %d, ih %d, cols %d, rows %d, px %d, cw %g, ch %g", iw, ih, cols, rows, px, cw , ch);
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  int prevseed = rand();
Ivan Mahonin fdbd7d
  gm->pz.turn = !!turn;
Ivan Mahonin fdbd7d
  gm->path = strprintf("%s", path);
Ivan Mahonin fdbd7d
  gm->imgname = strprintf("%s", imgname);
Ivan Mahonin fdbd7d
  gm->seed = seed;
Ivan Mahonin fdbd7d
  while(!gm->seed) gm->seed = rand();
Ivan Mahonin fdbd7d
  srand(gm->seed);
Ivan Mahonin fdbd7d
  puzzleAlloc(&gm->pz, rows, cols);
Ivan Mahonin fdbd7d
  puzzleGenLines(&gm->pz, cw, ch, 0.05, 1/5.0);
Ivan Mahonin fdbd7d
  puzzleGenChunks(&gm->pz);
Ivan Mahonin fdbd7d
  puzzleRenderChunks(&gm->pz, px, 0.3);
Ivan Mahonin fdbd7d
  srand(prevseed);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  gameSave(gm);
Ivan Mahonin fdbd7d
  return 1;
Ivan Mahonin fdbd7d
}
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
int gameSave(Game *gm) {
Ivan Mahonin fdbd7d
  char *tmp = strprintf("%s.tmp", gm->path);
Ivan Mahonin fdbd7d
  FILE *f = fopen(tmp, "w");
Ivan Mahonin fdbd7d
  if (!f) {
Ivan Mahonin fdbd7d
    ERR("gameSave: cannot open file for write: %s", tmp);
Ivan Mahonin fdbd7d
    free(tmp);
Ivan Mahonin fdbd7d
    return 0;
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  puzzleRecalcGroups(&gm->pz);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  // puzzle1
Ivan Mahonin fdbd7d
  // [[img-file]]
Ivan Mahonin fdbd7d
  // [rows] [cols] [groups] [seed]
Ivan Mahonin fdbd7d
  // [gid] [x] [y] [angle]...
Ivan Mahonin fdbd7d
  fprintf(f, "puzzle1\n[%s]\n%d %d %d %d %d\n", gm->imgname, gm->pz.rows, gm->pz.cols, !!gm->pz.turn, gm->pz.groups, gm->seed);
Ivan Mahonin fdbd7d
  Chunk *chunks = gm->pz.chunks[0];
Ivan Mahonin fdbd7d
  int cnt = gm->pz.rows * gm->pz.cols;
Ivan Mahonin fdbd7d
  for(int i = 0; i < cnt; ++i) {
Ivan Mahonin fdbd7d
    Chunk *c = &chunks[i];
Ivan Mahonin fdbd7d
    PhGroup *g = c->parent;
Ivan Mahonin fdbd7d
    Chunk *p = (Chunk*)((char*)g - offsetof(Chunk, group));
Ivan Mahonin fdbd7d
    int gid = p - chunks;
Ivan Mahonin fdbd7d
    if (p == c) fprintf(f, "%d %g %g %g\n", gid, g->o.x, g->o.y, vangled(g->dx));
Ivan Mahonin fdbd7d
           else fprintf(f, "%d\n", gid);
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  fflush(f);
Ivan Mahonin fdbd7d
  int err = ferror(f);
Ivan Mahonin fdbd7d
  if (err) ERR("gameSave: cannot write to file: %s", tmp);
Ivan Mahonin fdbd7d
  fclose(f);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  if (!err) {
Ivan Mahonin fdbd7d
    err = rename(tmp, gm->path);
Ivan Mahonin fdbd7d
    if (err) ERR("gameSave: cannot rename file: [%s] -> [%s]", tmp, gm->path);
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
  if (err) remove(tmp);
Ivan Mahonin fdbd7d
  free(tmp);
Ivan Mahonin fdbd7d
  return !err;
Ivan Mahonin fdbd7d
}
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
static FILE* internalLoadGameInfo(GameInfo *gi, const char *path) {
Ivan Mahonin fdbd7d
  FILE *f = fopen(path, "r");
Ivan Mahonin fdbd7d
  if (!f) {
Ivan Mahonin fdbd7d
    ERR("loadGameInfo: cannot open file for read: %s", path);
Ivan Mahonin fdbd7d
    return NULL;
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  size_t ls = 1;
Ivan Mahonin fdbd7d
  char *l = (char*)alloc(ls);
Ivan Mahonin fdbd7d
  getline(&l, &ls, f); trim(l);
Ivan Mahonin fdbd7d
  if (strcmp(l, "puzzle1")) {
Ivan Mahonin fdbd7d
    ERR("loadGameInfo: wrong file version (expected: puzzle1): %s", l);
Ivan Mahonin fdbd7d
    free(l); fclose(f); return NULL;
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  getline(&l, &ls, f); unquote(l, '[', ']');
Ivan Mahonin fdbd7d
  fscanf(f, "%d%d%d%d%d", &gi->rows, &gi->cols, &gi->turn, &gi->groups, &gi->seed);
Ivan Mahonin fdbd7d
  if ( gi->rows < 1 || gi->cols < 1 || gi->rows*gi->cols < 2
Ivan Mahonin fdbd7d
    || gi->turn < 0 || gi->turn > 1
Ivan Mahonin fdbd7d
    || gi->groups < 1 || gi->groups > gi->rows*gi->cols )
Ivan Mahonin fdbd7d
  {
Ivan Mahonin fdbd7d
    ERR("loadGameInfo: bad data in file: %s, [%dx%d, %d], %d", path, gi->rows, gi->cols, gi->turn, gi->groups);
Ivan Mahonin fdbd7d
    free(l); fclose(f); return NULL;
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  if (ferror(f)) {
Ivan Mahonin fdbd7d
    ERR("loadGameInfo: cannot read from file: %s", path);
Ivan Mahonin fdbd7d
    free(l); fclose(f); return NULL;
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  gi->imgname = l;
Ivan Mahonin fdbd7d
  return f;
Ivan Mahonin fdbd7d
}
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
GameInfo* loadGameInfo(const char *path) {
Ivan Mahonin fdbd7d
  GameInfo gi = { .t = filetime(path) };
Ivan Mahonin fdbd7d
  FILE *f = internalLoadGameInfo(&gi, path);
Ivan Mahonin fdbd7d
  if (!f) return NULL;
Ivan Mahonin fdbd7d
  fclose(f);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  GameInfo *r = alloc(ASIZEOF(*r) + strlen(gi.imgname) + 1);
Ivan Mahonin fdbd7d
  memcpy(r, &gi, sizeof(*r));
Ivan Mahonin fdbd7d
  r->imgname = strcpy((char*)(r+1), gi.imgname);
Ivan Mahonin fdbd7d
  return r;
Ivan Mahonin fdbd7d
}
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
int gameLoad(Game *gm, const char *path) {
Ivan Mahonin fdbd7d
  gameFree(gm);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  GameInfo gi = {};
Ivan Mahonin fdbd7d
  FILE *f = internalLoadGameInfo(&gi, path);
Ivan Mahonin fdbd7d
  if (!f) return ERR("gameLoad: cannot load game info: %s", path);
Ivan Mahonin fdbd7d
  if (!gameCreate(gm, path, gi.imgname, gi.rows, gi.cols, gi.turn, gi.seed)) {
Ivan Mahonin fdbd7d
    ERR("gameLoad: cannot create game: %s, %s, %dx%d, seed %d", path, gi.imgname, gi.rows, gi.cols, gi.seed);
Ivan Mahonin fdbd7d
    free(gi.imgname);
Ivan Mahonin fdbd7d
    fclose(f);
Ivan Mahonin fdbd7d
    return 0;
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  int cnt = gm->pz.rows * gm->pz.cols;
Ivan Mahonin fdbd7d
  Chunk *chunks = gm->pz.chunks[0];
Ivan Mahonin fdbd7d
  double* coords = (double*)alloc(cnt*3*ASIZEOF(double));
Ivan Mahonin fdbd7d
  for(int i = 0; i < cnt && !feof(f); ++i) {
Ivan Mahonin fdbd7d
    int gid = -1;
Ivan Mahonin fdbd7d
    fscanf(f, "%d", &gid);
Ivan Mahonin fdbd7d
    if (gid < 0 || gid >= cnt) break;
Ivan Mahonin fdbd7d
    
Ivan Mahonin fdbd7d
    Chunk *c = &chunks[i];
Ivan Mahonin fdbd7d
    Chunk *p = &chunks[gid];
Ivan Mahonin fdbd7d
    if (i == gid) {
Ivan Mahonin fdbd7d
      double v[3] = {};
Ivan Mahonin fdbd7d
      fscanf(f, "%lg%lg%lg", v+0, v+1, v+2);
Ivan Mahonin fdbd7d
      if (!(v[0] == v[0]) || !(v[1] == v[1]) || !(v[2] == v[2])) {
Ivan Mahonin fdbd7d
        WRN("gameLoad: invalid chunks positions");
Ivan Mahonin fdbd7d
        break;
Ivan Mahonin fdbd7d
      }
Ivan Mahonin fdbd7d
      memcpy(coords + i*3, v, sizeof(v));
Ivan Mahonin fdbd7d
    } else {
Ivan Mahonin fdbd7d
      chunkMerge(p, c);
Ivan Mahonin fdbd7d
    }    
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
  fclose(f);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  for(int i = 0; i < cnt; ++i) {
Ivan Mahonin fdbd7d
    Chunk *c = &chunks[i];
Ivan Mahonin fdbd7d
    if (c->parent != &c->group) continue;
Ivan Mahonin fdbd7d
    PhGroup *g = c->parent;
Ivan Mahonin fdbd7d
    g->o.x = coords[3*i + 0];
Ivan Mahonin fdbd7d
    g->o.y = coords[3*i + 1];
Ivan Mahonin fdbd7d
    g->dx = gm->pz.turn ? vcossind(coords[3*i + 2]) : vec(1,0);
Ivan Mahonin fdbd7d
    phGroupPlaceNodes(g);
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
  free(coords);
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  return 1;
Ivan Mahonin fdbd7d
}
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
void gameDraw(Game *gm) {
Ivan Mahonin fdbd7d
  int w = windowGetWidth();
Ivan Mahonin fdbd7d
  int h = windowGetHeight();
Ivan Mahonin fdbd7d
  Vec s = { w, h };
Ivan Mahonin fdbd7d
  saveState();
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  translate(w/2, h/2);
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  double pw = gm->pz.cols*gm->pz.cs.x;
Ivan Mahonin fdbd7d
  double ph = gm->pz.rows*gm->pz.cs.y;
Ivan Mahonin fdbd7d
  
Ivan Mahonin fdbd7d
  double k = mind(w/pw, h/ph);
Ivan Mahonin fdbd7d
  double kwin = k*0.9;
Ivan Mahonin fdbd7d
  k /= 1.5;
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  if (gameWon(gm)) {
Ivan Mahonin fdbd7d
    double dt = windowGetFrameTime();
Ivan Mahonin fdbd7d
    gm->winTransition += dt;
Ivan Mahonin fdbd7d
    if (gm->winTransition > 1) gm->winTransition = 1;
Ivan Mahonin fdbd7d
    
Ivan Mahonin fdbd7d
    PhGroup *g0 = gm->pz.ph.first;
Ivan Mahonin fdbd7d
    k = lintr(k, kwin, gm->winTransition);
Ivan Mahonin fdbd7d
    Vec o = vlintr(g0->o, vzero(), gm->winTransition);
Ivan Mahonin fdbd7d
    double a = lintr(vangled(g0->dx), 0, gm->winTransition);
Ivan Mahonin fdbd7d
    
Ivan Mahonin fdbd7d
    zoom(k);
Ivan Mahonin fdbd7d
    strokeWidth(3/k);
Ivan Mahonin fdbd7d
    translate(o.x, o.y);
Ivan Mahonin fdbd7d
    rotate(a);
Ivan Mahonin fdbd7d
    rectTextured(gm->pz.image, -pw/2, -ph/2, pw, ph);
Ivan Mahonin fdbd7d
  } else {
Ivan Mahonin fdbd7d
    zoom(k);
Ivan Mahonin fdbd7d
    strokeWidth(3/k);
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
    Vec mouse = { mouseTransformedX(), mouseTransformedY() };
Ivan Mahonin fdbd7d
    if (mouseWentDown("left"))
Ivan Mahonin fdbd7d
      puzzleChooseChunks(&gm->pz, mouse);
Ivan Mahonin fdbd7d
    if (mouseWentUp("left"))
Ivan Mahonin fdbd7d
      puzzleReleaseChunks(&gm->pz);
Ivan Mahonin fdbd7d
    puzzleUpdate(&gm->pz, vdiv(s, 2*k), mouse);
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
    int cnt = gm->pz.cols*gm->pz.rows;
Ivan Mahonin fdbd7d
    for(int i = cnt-1; i >= 0; --i) {
Ivan Mahonin fdbd7d
      chunkDraw(gm->pz.chunksOrder[i]);
Ivan Mahonin fdbd7d
      //chunkDrawDebug(gm->pz.chunksOrder[i]);
Ivan Mahonin fdbd7d
    }
Ivan Mahonin fdbd7d
    //physicsDrawDebug(&gm->pz.ph);
Ivan Mahonin fdbd7d
  }
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  restoreState();
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
  /*saveState();
Ivan Mahonin fdbd7d
  noFill();
Ivan Mahonin fdbd7d
  stroke(COLOR_WHITE);
Ivan Mahonin fdbd7d
  textf(10, 10, "fps: %f", 1/windowGetFrameTime());
Ivan Mahonin fdbd7d
  restoreState();*/
Ivan Mahonin fdbd7d
}
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d
Ivan Mahonin fdbd7d