diff src/debrief.c @ 0:c84446dfb3f5

initial add
author paulo@localhost
date Fri, 13 Mar 2009 00:39:12 -0700
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/debrief.c	Fri Mar 13 00:39:12 2009 -0700
     1.3 @@ -0,0 +1,603 @@
     1.4 +/* PC frontend for LOCKJAW, an implementation of the Soviet Mind Game
     1.5 +
     1.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net>
     1.7 +
     1.8 +This work is free software; you can redistribute it and/or modify
     1.9 +it under the terms of the GNU General Public License as published by
    1.10 +the Free Software Foundation; either version 2 of the License, or
    1.11 +(at your option) any later version.
    1.12 +
    1.13 +This program is distributed in the hope that it will be useful,
    1.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.16 +GNU General Public License for more details.
    1.17 +
    1.18 +You should have received a copy of the GNU General Public License
    1.19 +along with this program; if not, write to the Free Software
    1.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.21 +
    1.22 +Original game concept and design by Alexey Pajitnov.
    1.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
    1.24 +or The Tetris Company LLC.
    1.25 +
    1.26 +*/
    1.27 +#include "ljplay.h"
    1.28 +#include "options.h"
    1.29 +#include <stdio.h>
    1.30 +#ifndef NO_DATETIME
    1.31 +#include <time.h>
    1.32 +#endif
    1.33 +
    1.34 +#define USING_DEBRIEF_PAGE 1
    1.35 +#define MIN_RECTUM_ROWS 4
    1.36 +#define MIN_ZIGZAG_ROWS 2
    1.37 +
    1.38 +// On newlib, use a variant of sprintf that doesn't handle floating
    1.39 +// point formats. On other C libraries, use standard sprintf.
    1.40 +#ifdef HAS_FPU
    1.41 +#define siprintf sprintf
    1.42 +#ifndef HAS_FOPEN
    1.43 +#define HAS_FOPEN
    1.44 +#endif
    1.45 +#define DEBRIEF_TO_STDOUT
    1.46 +#endif
    1.47 +
    1.48 +#ifdef HAS_FOPEN
    1.49 +#include "ljpath.h"  // for ljfopen
    1.50 +#endif
    1.51 +
    1.52 +const char *const debriefBoolNames[2] = {
    1.53 +  "Off", "On"
    1.54 +};
    1.55 +
    1.56 +typedef struct TimeResultMSF {
    1.57 +  unsigned int pieces_100m;
    1.58 +  unsigned int garbage_100m;
    1.59 +  unsigned int minutes;
    1.60 +  unsigned char seconds;
    1.61 +  unsigned char frames;
    1.62 +} TimeResultMSF;
    1.63 +
    1.64 +void frames2msf(unsigned int gameTime,
    1.65 +                unsigned int nPieces,
    1.66 +                unsigned int outGarbage,
    1.67 +                TimeResultMSF *out) {
    1.68 +  unsigned int gameSeconds = gameTime / 60U;
    1.69 +  unsigned int gameMinutes = gameSeconds / 60U;
    1.70 +  out->pieces_100m = gameTime
    1.71 +                     ? 360000ULL * nPieces / gameTime
    1.72 +                     : 0;
    1.73 +  out->garbage_100m = gameTime
    1.74 +                      ? 360000ULL * outGarbage / gameTime
    1.75 +                      : 0;
    1.76 +  out->minutes = gameMinutes;
    1.77 +  out->seconds = gameSeconds - gameMinutes * 60U;
    1.78 +  out->frames = gameTime - gameSeconds * 60U;
    1.79 +}
    1.80 +
    1.81 +static unsigned int countBlocksLeft(const LJField *p) {
    1.82 +  unsigned int n = 0;
    1.83 +
    1.84 +  for (int y = 0; y < LJ_PF_HT; ++y) {
    1.85 +    for (int x = 0; x < LJ_PF_WID; ++x) {
    1.86 +      if (p->b[y][x]) {
    1.87 +        ++n;
    1.88 +      }
    1.89 +    }
    1.90 +  }
    1.91 +  return n;
    1.92 +}
    1.93 +
    1.94 +/**
    1.95 + * Calculates the number of rows starting at the bottom that
    1.96 + * conform to a zigzag pattern.
    1.97 + * @param p the playfield to test
    1.98 + * @return the number of rows successfully completed
    1.99 + */
   1.100 +unsigned int calcZigzagGrade(const LJField *p) {
   1.101 +  unsigned int hole = p->leftWall;
   1.102 +  int delta = 1;
   1.103 +
   1.104 +  for (size_t y = 0; y < p->ceiling; ++y) {
   1.105 +
   1.106 +    // invariant:
   1.107 +    // at this point, y equals the number of rows known to conform
   1.108 +    for (size_t x = p->leftWall; x < p->rightWall; ++x) {
   1.109 +      unsigned int blk = p->b[y][x];
   1.110 +
   1.111 +      // A block should be in all cells of the row
   1.112 +      // except for the hole cell, which should be empty.
   1.113 +      if (x == hole) {
   1.114 +
   1.115 +        // if there's a block where a hole should be, this row
   1.116 +        // doesn't conform, but all the rows below do conform
   1.117 +        if (blk) {
   1.118 +          return y;
   1.119 +        }
   1.120 +      } else {
   1.121 +
   1.122 +        // if there's a hole where a block should be, this row
   1.123 +        // doesn't conform, but all the rows below do conform
   1.124 +        if (!blk) {
   1.125 +          return y;
   1.126 +        }
   1.127 +      }
   1.128 +    }
   1.129 +
   1.130 +    // if this hole isn't covered up on the next row,
   1.131 +    // this row doesn't conform either
   1.132 +    // changed in 0.43 after a clarification from Kitaru
   1.133 +    if (!p->b[y + 1][hole]) {
   1.134 +      return y;
   1.135 +    }
   1.136 +
   1.137 +    // by now we know that the row conforms,
   1.138 +    // so move the hole for the next row
   1.139 +    if (hole == p->rightWall - 1) {
   1.140 +      delta = -1;
   1.141 +    } else if (hole == p->leftWall) {
   1.142 +      delta = 1;
   1.143 +    }
   1.144 +    hole += delta;
   1.145 +
   1.146 +  }
   1.147 +  return p->ceiling;
   1.148 +}
   1.149 +
   1.150 +
   1.151 +/**
   1.152 + * Calculates the number of rows in a playfield that were prepared
   1.153 + * for an I tetromino.
   1.154 + * Rule for this pattern:
   1.155 + * 1. Only one hole on the bottom row.
   1.156 + * 2. This column must be unoccupied below the ceiling.
   1.157 + * 3. Conforming rows must be full except for the hole.
   1.158 + *
   1.159 + * Easy test procedure for this pattern:
   1.160 + * Level 4 garbage
   1.161 + * @return the number of rows that conform to constraint 3, or 0 if the
   1.162 + * field does not conform to constraint 1 or 2.
   1.163 + */
   1.164 +unsigned int calcRectumGrade(const LJField *p) {
   1.165 +  unsigned int hole = LJ_PF_WID;
   1.166 +
   1.167 +  // search for the first hole on the bottom row
   1.168 +  for (unsigned int x = p->leftWall;
   1.169 +       x < p->rightWall;
   1.170 +       ++x) {
   1.171 +    if (!p->b[0][x]) {
   1.172 +      hole = x;
   1.173 +      break;
   1.174 +    }
   1.175 +  }
   1.176 +
   1.177 +  // If there is no hole in the bottom row, then 0 rows conform.
   1.178 +  // This shouldn't happen in standard smg because the line would be
   1.179 +  // cleared, but eventually LJ will support games other than SMG,
   1.180 +  // and checking the array bounds is O(1), so why not?
   1.181 +  if (hole >= p->rightWall) {
   1.182 +    return 0;
   1.183 +  }
   1.184 +
   1.185 +  // make sure that the row is clear through the whole visible
   1.186 +  // portion of the playfield
   1.187 +  for (unsigned int y = 0; y < p->ceiling; ++y) {
   1.188 +
   1.189 +    // If this column isn't empty, the whole field doesn't conform.
   1.190 +    if (p->b[y][hole]) {
   1.191 +      return 0;
   1.192 +    }
   1.193 +  }
   1.194 +
   1.195 +  for (unsigned int y = 0; y < p->ceiling; ++y) {
   1.196 +
   1.197 +    // At this point, the bottom y rows conform.
   1.198 +    for (unsigned int x = p->leftWall; x < p->rightWall; ++x) {
   1.199 +
   1.200 +      // Disregarding the hole column, if there's an empty cell
   1.201 +      // in this row, then this row does not conform.
   1.202 +      if (x != hole && !p->b[y][x]) {
   1.203 +        return y;
   1.204 +      }
   1.205 +    }
   1.206 +  }
   1.207 +  return p->ceiling;
   1.208 +}
   1.209 +
   1.210 +/**
   1.211 + * Converts a rank number (0-19) to a text rank.
   1.212 + * 0-9: 10-1 kyu; 10-99: 1-90 dan; 100+: grandmaster
   1.213 + * @return number of characters written
   1.214 + */
   1.215 +static size_t printSecretGrade(char *dst, int rank) {
   1.216 +  if (rank >= 100) {
   1.217 +    return siprintf(dst, "GM");
   1.218 +  } else if (rank >= 10) {
   1.219 +    return siprintf(dst, "S%d", rank - 9);
   1.220 +  } else {
   1.221 +    return siprintf(dst, "%d", 10 - rank);
   1.222 +  }
   1.223 +}
   1.224 +
   1.225 +static size_t printFracG(char *dst, unsigned int value) {
   1.226 +  if (value == 0) {
   1.227 +    return siprintf(dst, "Instant");
   1.228 +  } else if (value == 1) {
   1.229 +    return siprintf(dst, "1G");
   1.230 +  } else {
   1.231 +    return siprintf(dst, "1/%dG", value);
   1.232 +  }
   1.233 +}
   1.234 +
   1.235 +#include <string.h>  // remove once fourcc transition is complete
   1.236 +static size_t printFourCC(char *dst, FourCC f) {
   1.237 +  const char *data = ljGetFourCCName(f);
   1.238 +  if (data) {
   1.239 +    int pos = 0;
   1.240 +    for(pos = 0; *data; ++pos) {
   1.241 +      dst[pos] = *data++;
   1.242 +    }
   1.243 +    return pos;
   1.244 +  } else {
   1.245 +    int pos = 0;
   1.246 +    for(pos = 0; f.c[pos] && pos < 4; ++pos) {
   1.247 +      dst[pos] = f.c[pos];
   1.248 +    }
   1.249 +    return pos;
   1.250 +  }
   1.251 +}
   1.252 +
   1.253 +size_t buildOptionsReportPage(char *dst, const LJView *v) {
   1.254 +  size_t pos = 0;
   1.255 +  
   1.256 +
   1.257 +#if USING_DEBRIEF_PAGE
   1.258 +  const LJField *p = v->field;
   1.259 +
   1.260 +  pos += siprintf(dst + pos,
   1.261 +                  "Options\n\n"
   1.262 +                  "Well: %dx%d%s, Enter: %s\n"
   1.263 +                  "Speed: ",
   1.264 +                  p->rightWall - p->leftWall,
   1.265 +                  p->ceiling,
   1.266 +                  v->hidePF ? " invisible" : "",
   1.267 +                  p->enterAbove ? "Above" : "Below");
   1.268 +  pos += printFourCC(dst + pos, 
   1.269 +             optionsSpeedCurveNames[(size_t)p->speedState.curve]);
   1.270 +  pos += siprintf(dst + pos,
   1.271 +                  ", ARE: %d ms\n"
   1.272 +                  "Randomizer: ",
   1.273 +                  v->field->areStyle * 50 / 3);
   1.274 +  pos += printFourCC(dst + pos, 
   1.275 +                     optionsRandNames[(size_t)p->randomizer]);
   1.276 +  pos += siprintf(dst + pos, " of ");
   1.277 +  pos += printFourCC(dst + pos, 
   1.278 +                     optionsPieceSetNames[(size_t)p->pieceSet]);
   1.279 +  pos += siprintf(dst + pos, "\nHold: ");
   1.280 +  pos += printFourCC(dst + pos, 
   1.281 +                     optionsHoldStyleNames[(size_t)p->holdStyle]);
   1.282 +  pos += siprintf(dst + pos, ", Rotation: ");
   1.283 +  pos += printFourCC(dst + pos, 
   1.284 +                     optionsRotNames[(size_t)p->rotationSystem]);
   1.285 +  if (p->maxUpwardKicks < 20) {
   1.286 +    pos += siprintf(dst + pos,
   1.287 +                    " %d FK",
   1.288 +                    (int)p->maxUpwardKicks);
   1.289 +  }
   1.290 +  if (v->control->initialRotate) {
   1.291 +    pos += siprintf(dst + pos, "+initial");
   1.292 +  }
   1.293 +
   1.294 +  pos += siprintf(dst + pos,
   1.295 +                  "\nLock: ");
   1.296 +  if (p->setLockDelay >= 128) {
   1.297 +    pos += siprintf(dst + pos, "Never");
   1.298 +  } else {
   1.299 +    if (p->setLockDelay > 0) {
   1.300 +      pos += siprintf(dst + pos,
   1.301 +                       "%d ms ",
   1.302 +                       p->setLockDelay * 50 / 3);
   1.303 +    }
   1.304 +    pos += printFourCC(dst + pos, 
   1.305 +                       optionsLockdownNames[(size_t)p->lockReset]);
   1.306 +  }
   1.307 +  pos += siprintf(dst + pos,
   1.308 +                   ", Deep: %s\n",
   1.309 +                   debriefBoolNames[p->bottomBlocks]);
   1.310 +
   1.311 +  pos += siprintf(dst + pos,
   1.312 +                  "Line clear: %d ms ",
   1.313 +                  p->speed.lineDelay * 50 / 3);
   1.314 +  pos += printFourCC(dst + pos,
   1.315 +                     optionsGravNames[(size_t)p->clearGravity]);
   1.316 +  pos += siprintf(dst + pos,
   1.317 +                  ", Gluing: ");
   1.318 +  pos += printFourCC(dst + pos,
   1.319 +                     optionsGluingNames[(size_t)p->gluing]);
   1.320 +  pos += siprintf(dst + pos,
   1.321 +                  "\nDrop score: ");
   1.322 +  pos += printFourCC(dst + pos,
   1.323 +                optionsDropScoringNames[(size_t)p->dropScoreStyle]);
   1.324 +  pos += siprintf(dst + pos,
   1.325 +                  ", T-spin: ");
   1.326 +  pos += printFourCC(dst + pos,
   1.327 +                     optionsTspinNames[(size_t)p->tSpinAlgo]);
   1.328 +  pos += siprintf(dst + pos,
   1.329 +                  "\nGarbage: ");
   1.330 +  pos += printFourCC(dst + pos,
   1.331 +                     optionsGarbageNames[(size_t)p->garbageStyle]);
   1.332 +  pos += siprintf(dst + pos,
   1.333 +                   ", DAS: %d ms ",
   1.334 +                   v->control->dasDelay * 50 / 3);
   1.335 +  pos += printFracG(dst + pos, v->control->dasSpeed);
   1.336 +  pos += siprintf(dst + pos,
   1.337 +                   "\nSoft drop: ");
   1.338 +  pos += printFracG(dst + pos, v->control->softDropSpeed + 1);
   1.339 +  dst[pos++] = ' ';
   1.340 +  pos += printFourCC(dst + pos,
   1.341 +                     optionsZangiNames[v->control->softDropLock]);
   1.342 +  pos += siprintf(dst + pos,
   1.343 +                  ", Hard drop: ");
   1.344 +  pos += printFourCC(dst + pos,
   1.345 +                     optionsZangiNames[v->control->hardDropLock]);
   1.346 +  pos += siprintf(dst + pos, "\nShadow: ");
   1.347 +  pos += printFourCC(dst + pos,
   1.348 +                     optionsShadowNames[v->hideShadow]);
   1.349 +  pos += siprintf(dst + pos,
   1.350 +                  ", Next: %d\n",
   1.351 +                  v->nextPieces);
   1.352 +#else
   1.353 +  pos += siprintf(dst, "\n\nDebrief disabled.\n");
   1.354 +#endif
   1.355 +
   1.356 +  dst[pos] = 0;
   1.357 +  return pos;
   1.358 +}
   1.359 +
   1.360 +#ifdef DEBRIEF_SHORT
   1.361 +static const char *const naiveGravity[8] = {
   1.362 +  "1L", "2L", "3L", "4L",
   1.363 +  "T1", "T2", "T3"
   1.364 +};
   1.365 +static const char *const cascadeGravity[8] = {
   1.366 +  "1L", "2L", "3L", "4L",
   1.367 +  "5L", "6L", "7L", "8L+"
   1.368 +};
   1.369 +#else
   1.370 +static const char *const naiveGravity[8] = {
   1.371 +  "single", "double", "triple", "home run",
   1.372 +  "T single", "T double", "T triple"
   1.373 +};
   1.374 +static const char *const cascadeGravity[8] = {
   1.375 +  "single", "double", "triple", "quad",
   1.376 +  "5L", "6L", "7L", "8L+"
   1.377 +};
   1.378 +#endif
   1.379 +
   1.380 +static size_t printLineCounts(char *dst, const LJField *p) {
   1.381 +  size_t pos = 0;
   1.382 +  const char *const *names = (p->clearGravity == LJGRAV_NAIVE)
   1.383 +                             ? naiveGravity : cascadeGravity;
   1.384 +  dst[pos++] = '(';
   1.385 +  for (int i = 0; i < 8; ++i) {
   1.386 +    if (names[i]) {
   1.387 +      pos += siprintf(dst + pos,
   1.388 +                      "%s%s: %u",
   1.389 +                      i ? "; " : "",
   1.390 +                      names[i],
   1.391 +                      p->nLineClears[i]);
   1.392 +    }
   1.393 +  }
   1.394 +  dst[pos++] = ')';
   1.395 +  dst[pos++] = '\n';
   1.396 +  return pos;
   1.397 +}
   1.398 +
   1.399 +
   1.400 +size_t buildDebriefPage(char *dst, const LJView *v) {
   1.401 +  size_t pos = 0;
   1.402 +
   1.403 +#if USING_DEBRIEF_PAGE
   1.404 +#ifndef NO_DATETIME
   1.405 +  const time_t finishTimeUNIX = time(NULL);
   1.406 +  const struct tm *finishTime = localtime(&finishTimeUNIX);
   1.407 +  char finishTimeStr[64];
   1.408 +  
   1.409 +  /* I would have used
   1.410 +  strftime(finishTimeStr, sizeof(finishTimeStr), "%Y-%m-%d at %H:%M", finishTime);
   1.411 +  but it brings in the floating-point library on GBA/DS. */
   1.412 +  siprintf(finishTimeStr,
   1.413 +           "%04d-%02d-%02d at %02d:%02d",
   1.414 +           finishTime->tm_year + 1900,
   1.415 +           finishTime->tm_mon + 1, finishTime->tm_mday,
   1.416 +           finishTime->tm_hour, finishTime->tm_min);
   1.417 +#endif
   1.418 +
   1.419 +  const LJField *p = v->field;
   1.420 +  unsigned long int keys_100m = p->nPieces
   1.421 +                                ? 100U * v->control->presses / p->nPieces
   1.422 +                                : 666;
   1.423 +  TimeResultMSF gameMSF, activeMSF;
   1.424 +  const char *wordPieces = (p->nPieces != 1) ? "tetrominoes" : "tetromino";
   1.425 +  if (p->pieceSet == LJRAND_234BLK) {
   1.426 +    wordPieces = (p->nPieces != 1) ? "pieces" : "piece";
   1.427 +  }
   1.428 +  
   1.429 +  /* Secret grades */
   1.430 +  unsigned int nBlocksLeft = countBlocksLeft(p);
   1.431 +  unsigned int nZigzagRows = calcZigzagGrade(p);
   1.432 +  unsigned int nRectumRows = calcRectumGrade(p);
   1.433 +
   1.434 +  pos += siprintf(dst + pos,
   1.435 +                   "Result:\n\n%s ",
   1.436 +                   v->control->countdown <= 0 ? "Cleared" : "Stopped");
   1.437 +  pos += printFourCC(dst + pos, gimmickNames[p->gimmick]);
   1.438 +  pos += siprintf(dst + pos,
   1.439 +                  " at level %d\n",
   1.440 +                  p->speedState.level);
   1.441 +#if !defined(NO_DATETIME) || defined(WITH_REPLAY)
   1.442 +#ifndef NO_DATETIME
   1.443 +  pos += siprintf(dst + pos, "on %s", finishTimeStr);
   1.444 +#endif
   1.445 +#ifdef WITH_REPLAY
   1.446 +  if (p->reloaded) {
   1.447 +    pos += siprintf(dst + pos, " from saved state");
   1.448 +  }
   1.449 +#endif
   1.450 +  dst[pos++] = '\n';
   1.451 +#endif
   1.452 +
   1.453 +  frames2msf(p->gameTime, p->nPieces, p->outGarbage, &gameMSF);
   1.454 +  frames2msf(p->activeTime, p->nPieces, p->outGarbage, &activeMSF);
   1.455 +
   1.456 +  pos += siprintf(dst + pos,
   1.457 +                   "Played %d %s in %u:%02u.%02u (%u.%02u/min)\n",
   1.458 +                   p->nPieces,
   1.459 +                   wordPieces,
   1.460 +                   gameMSF.minutes,
   1.461 +                   (unsigned int)gameMSF.seconds,
   1.462 +                   gameMSF.frames * 5U / 3U,
   1.463 +                   (unsigned int) (gameMSF.pieces_100m / 100),
   1.464 +                   (unsigned int) (gameMSF.pieces_100m % 100));
   1.465 +  pos += siprintf(dst + pos,
   1.466 +                   "(active time only: %u:%02u.%02u, %u.%02u/min)\n",
   1.467 +                   activeMSF.minutes,
   1.468 +                   (unsigned int)activeMSF.seconds,
   1.469 +                   activeMSF.frames * 5U / 3U,
   1.470 +                   (unsigned int) (activeMSF.pieces_100m / 100),
   1.471 +                   (unsigned int) (activeMSF.pieces_100m % 100));
   1.472 +  pos += siprintf(dst + pos,
   1.473 +                   "Pressed %u keys (%u.%02u/piece)\n\n",
   1.474 +                   v->control->presses,
   1.475 +                   (unsigned int) (keys_100m / 100),
   1.476 +                   (unsigned int) (keys_100m % 100));
   1.477 +
   1.478 +  pos += siprintf(dst + pos,
   1.479 +                  "Made %u lines",
   1.480 +                  p->lines);
   1.481 +  if (p->gluing == LJGLUING_SQUARE) {
   1.482 +    pos += siprintf(dst + pos,
   1.483 +                    " and %u pure + %u squares",
   1.484 +                    p->monosquares, p->multisquares);
   1.485 +  }
   1.486 +  dst[pos++] = '\n';
   1.487 +  pos += printLineCounts(dst + pos, p);
   1.488 +  pos += siprintf(dst + pos,
   1.489 +                   "Sent %u garbage (%u.%02u per minute)\n",
   1.490 +                   p->outGarbage,
   1.491 +                   (unsigned int) (gameMSF.garbage_100m / 100),
   1.492 +                   (unsigned int) (gameMSF.garbage_100m % 100));
   1.493 +  if (nZigzagRows >= MIN_ZIGZAG_ROWS) {
   1.494 +    pos += siprintf(dst + pos,
   1.495 +                    "Made %u rows of > for grade ",
   1.496 +                    nZigzagRows);
   1.497 +    pos += printSecretGrade(dst + pos,
   1.498 +                            nZigzagRows >= 19 ? 100 : nZigzagRows);
   1.499 +  } else if (nRectumRows >= MIN_RECTUM_ROWS) {
   1.500 +    pos += siprintf(dst + pos,
   1.501 +                    "Made %u-row rectum for grade ",
   1.502 +                    nRectumRows);
   1.503 +    pos += printSecretGrade(dst + pos,
   1.504 +                            nRectumRows >= 20 ? 100 : nRectumRows - 1);
   1.505 +  } else if (nBlocksLeft >= 110 && nBlocksLeft <= 111) {
   1.506 +    pos += siprintf(dst + pos,
   1.507 +                    "Secret grade: Eleventy%s",
   1.508 +                    (nBlocksLeft & 1) ? "-one" : "");
   1.509 +  } else {
   1.510 +    pos += siprintf(dst + pos,
   1.511 +                    "Left %d blocks behind",
   1.512 +                    nBlocksLeft);
   1.513 +  }
   1.514 +  dst[pos++] = '\n';
   1.515 +  dst[pos++] = '\n';
   1.516 +  pos += printFourCC(dst + pos, optionsScoringNames[p->scoreStyle]);
   1.517 +
   1.518 +  pos += siprintf(dst + pos,
   1.519 +                   " score: %d\n",
   1.520 +                   p->score);
   1.521 +  
   1.522 +      /*
   1.523 +      tod stats not in lj
   1.524 +
   1.525 +        points/line, 40 lines score, silver squares, gold squares
   1.526 +        
   1.527 +        */
   1.528 +
   1.529 +#else
   1.530 +  pos += siprintf(dst, "\n\nDebrief disabled. Score: %u\n", v->field->score);
   1.531 +#endif
   1.532 +  return pos;
   1.533 +}
   1.534 +
   1.535 +void debriefDrawPage(const char *page, size_t pageNumber);
   1.536 +LJBits debriefHandleKeys(void);
   1.537 +extern volatile char redrawWholeScreen;
   1.538 +
   1.539 +
   1.540 +/**
   1.541 + * Reports the player's performance.
   1.542 + */
   1.543 +void debrief(LJView *v) {
   1.544 +  char pageData[2000];  // current length is under 1000 chars
   1.545 +  char *page[2] = {pageData, "Page coming soon!"};
   1.546 +  int curPage = 0;
   1.547 +  
   1.548 +  {
   1.549 +    size_t pos;
   1.550 +
   1.551 +    pos = buildDebriefPage(page[0], v);
   1.552 +    page[1] = page[0] + (++pos);
   1.553 +    pos += buildOptionsReportPage(page[1], v);
   1.554 +  }
   1.555 +#ifdef HAS_FOPEN
   1.556 +  FILE *logFile = ljfopen("lj-scores.txt", "at");
   1.557 +  if (logFile) {
   1.558 +    fputs("\n\n\n", logFile);
   1.559 +    fputs(page[0], logFile);
   1.560 +    fputs(page[1], logFile);
   1.561 +#ifdef DEBRIEF_TO_STDOUT
   1.562 +    fputs(page[0], stdout);
   1.563 +    fputs(page[1], stdout);
   1.564 +#endif
   1.565 +    fclose(logFile);
   1.566 +  }
   1.567 +#endif
   1.568 +
   1.569 +  LJBits lastKeys = ~0;
   1.570 +  redrawWholeScreen = 1;
   1.571 +  debriefHandleKeys();  // call once to clear key buffer
   1.572 +
   1.573 +  for (;;) {
   1.574 +    LJBits sounds = 0;
   1.575 +
   1.576 +    if (redrawWholeScreen) {
   1.577 +      redrawWholeScreen = 0;
   1.578 +      debriefDrawPage(page[curPage], curPage);
   1.579 +    }
   1.580 +    LJBits keys = debriefHandleKeys();
   1.581 +    LJBits newKeys = keys & ~lastKeys;
   1.582 +
   1.583 +    if (newKeys & VKEY_LEFT) {
   1.584 +      if (curPage > 0) {
   1.585 +        curPage -= 1;
   1.586 +        redrawWholeScreen = 1;
   1.587 +        sounds |= LJSND_HOLD;
   1.588 +      }
   1.589 +    }
   1.590 +    
   1.591 +    if (newKeys & VKEY_RIGHT) {
   1.592 +      if (curPage + 1 < sizeof(page) / sizeof(page[0])) {
   1.593 +        curPage += 1;
   1.594 +        redrawWholeScreen = 1;
   1.595 +        sounds |= LJSND_HOLD;
   1.596 +      }
   1.597 +    }
   1.598 +    
   1.599 +    if (newKeys & (VKEY_ROTR | VKEY_ROTL)) {
   1.600 +      break;
   1.601 +    } else {
   1.602 +      lastKeys = keys;
   1.603 +    }
   1.604 +    playSoundEffects(v, sounds, 100);
   1.605 +  }
   1.606 +}