annotate src/debrief.c @ 0:c84446dfb3f5

initial add
author paulo@localhost
date Fri, 13 Mar 2009 00:39:12 -0700
parents
children
rev   line source
paulo@0 1 /* PC frontend for LOCKJAW, an implementation of the Soviet Mind Game
paulo@0 2
paulo@0 3 Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net>
paulo@0 4
paulo@0 5 This work is free software; you can redistribute it and/or modify
paulo@0 6 it under the terms of the GNU General Public License as published by
paulo@0 7 the Free Software Foundation; either version 2 of the License, or
paulo@0 8 (at your option) any later version.
paulo@0 9
paulo@0 10 This program is distributed in the hope that it will be useful,
paulo@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
paulo@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
paulo@0 13 GNU General Public License for more details.
paulo@0 14
paulo@0 15 You should have received a copy of the GNU General Public License
paulo@0 16 along with this program; if not, write to the Free Software
paulo@0 17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
paulo@0 18
paulo@0 19 Original game concept and design by Alexey Pajitnov.
paulo@0 20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
paulo@0 21 or The Tetris Company LLC.
paulo@0 22
paulo@0 23 */
paulo@0 24 #include "ljplay.h"
paulo@0 25 #include "options.h"
paulo@0 26 #include <stdio.h>
paulo@0 27 #ifndef NO_DATETIME
paulo@0 28 #include <time.h>
paulo@0 29 #endif
paulo@0 30
paulo@0 31 #define USING_DEBRIEF_PAGE 1
paulo@0 32 #define MIN_RECTUM_ROWS 4
paulo@0 33 #define MIN_ZIGZAG_ROWS 2
paulo@0 34
paulo@0 35 // On newlib, use a variant of sprintf that doesn't handle floating
paulo@0 36 // point formats. On other C libraries, use standard sprintf.
paulo@0 37 #ifdef HAS_FPU
paulo@0 38 #define siprintf sprintf
paulo@0 39 #ifndef HAS_FOPEN
paulo@0 40 #define HAS_FOPEN
paulo@0 41 #endif
paulo@0 42 #define DEBRIEF_TO_STDOUT
paulo@0 43 #endif
paulo@0 44
paulo@0 45 #ifdef HAS_FOPEN
paulo@0 46 #include "ljpath.h" // for ljfopen
paulo@0 47 #endif
paulo@0 48
paulo@0 49 const char *const debriefBoolNames[2] = {
paulo@0 50 "Off", "On"
paulo@0 51 };
paulo@0 52
paulo@0 53 typedef struct TimeResultMSF {
paulo@0 54 unsigned int pieces_100m;
paulo@0 55 unsigned int garbage_100m;
paulo@0 56 unsigned int minutes;
paulo@0 57 unsigned char seconds;
paulo@0 58 unsigned char frames;
paulo@0 59 } TimeResultMSF;
paulo@0 60
paulo@0 61 void frames2msf(unsigned int gameTime,
paulo@0 62 unsigned int nPieces,
paulo@0 63 unsigned int outGarbage,
paulo@0 64 TimeResultMSF *out) {
paulo@0 65 unsigned int gameSeconds = gameTime / 60U;
paulo@0 66 unsigned int gameMinutes = gameSeconds / 60U;
paulo@0 67 out->pieces_100m = gameTime
paulo@0 68 ? 360000ULL * nPieces / gameTime
paulo@0 69 : 0;
paulo@0 70 out->garbage_100m = gameTime
paulo@0 71 ? 360000ULL * outGarbage / gameTime
paulo@0 72 : 0;
paulo@0 73 out->minutes = gameMinutes;
paulo@0 74 out->seconds = gameSeconds - gameMinutes * 60U;
paulo@0 75 out->frames = gameTime - gameSeconds * 60U;
paulo@0 76 }
paulo@0 77
paulo@0 78 static unsigned int countBlocksLeft(const LJField *p) {
paulo@0 79 unsigned int n = 0;
paulo@0 80
paulo@0 81 for (int y = 0; y < LJ_PF_HT; ++y) {
paulo@0 82 for (int x = 0; x < LJ_PF_WID; ++x) {
paulo@0 83 if (p->b[y][x]) {
paulo@0 84 ++n;
paulo@0 85 }
paulo@0 86 }
paulo@0 87 }
paulo@0 88 return n;
paulo@0 89 }
paulo@0 90
paulo@0 91 /**
paulo@0 92 * Calculates the number of rows starting at the bottom that
paulo@0 93 * conform to a zigzag pattern.
paulo@0 94 * @param p the playfield to test
paulo@0 95 * @return the number of rows successfully completed
paulo@0 96 */
paulo@0 97 unsigned int calcZigzagGrade(const LJField *p) {
paulo@0 98 unsigned int hole = p->leftWall;
paulo@0 99 int delta = 1;
paulo@0 100
paulo@0 101 for (size_t y = 0; y < p->ceiling; ++y) {
paulo@0 102
paulo@0 103 // invariant:
paulo@0 104 // at this point, y equals the number of rows known to conform
paulo@0 105 for (size_t x = p->leftWall; x < p->rightWall; ++x) {
paulo@0 106 unsigned int blk = p->b[y][x];
paulo@0 107
paulo@0 108 // A block should be in all cells of the row
paulo@0 109 // except for the hole cell, which should be empty.
paulo@0 110 if (x == hole) {
paulo@0 111
paulo@0 112 // if there's a block where a hole should be, this row
paulo@0 113 // doesn't conform, but all the rows below do conform
paulo@0 114 if (blk) {
paulo@0 115 return y;
paulo@0 116 }
paulo@0 117 } else {
paulo@0 118
paulo@0 119 // if there's a hole where a block should be, this row
paulo@0 120 // doesn't conform, but all the rows below do conform
paulo@0 121 if (!blk) {
paulo@0 122 return y;
paulo@0 123 }
paulo@0 124 }
paulo@0 125 }
paulo@0 126
paulo@0 127 // if this hole isn't covered up on the next row,
paulo@0 128 // this row doesn't conform either
paulo@0 129 // changed in 0.43 after a clarification from Kitaru
paulo@0 130 if (!p->b[y + 1][hole]) {
paulo@0 131 return y;
paulo@0 132 }
paulo@0 133
paulo@0 134 // by now we know that the row conforms,
paulo@0 135 // so move the hole for the next row
paulo@0 136 if (hole == p->rightWall - 1) {
paulo@0 137 delta = -1;
paulo@0 138 } else if (hole == p->leftWall) {
paulo@0 139 delta = 1;
paulo@0 140 }
paulo@0 141 hole += delta;
paulo@0 142
paulo@0 143 }
paulo@0 144 return p->ceiling;
paulo@0 145 }
paulo@0 146
paulo@0 147
paulo@0 148 /**
paulo@0 149 * Calculates the number of rows in a playfield that were prepared
paulo@0 150 * for an I tetromino.
paulo@0 151 * Rule for this pattern:
paulo@0 152 * 1. Only one hole on the bottom row.
paulo@0 153 * 2. This column must be unoccupied below the ceiling.
paulo@0 154 * 3. Conforming rows must be full except for the hole.
paulo@0 155 *
paulo@0 156 * Easy test procedure for this pattern:
paulo@0 157 * Level 4 garbage
paulo@0 158 * @return the number of rows that conform to constraint 3, or 0 if the
paulo@0 159 * field does not conform to constraint 1 or 2.
paulo@0 160 */
paulo@0 161 unsigned int calcRectumGrade(const LJField *p) {
paulo@0 162 unsigned int hole = LJ_PF_WID;
paulo@0 163
paulo@0 164 // search for the first hole on the bottom row
paulo@0 165 for (unsigned int x = p->leftWall;
paulo@0 166 x < p->rightWall;
paulo@0 167 ++x) {
paulo@0 168 if (!p->b[0][x]) {
paulo@0 169 hole = x;
paulo@0 170 break;
paulo@0 171 }
paulo@0 172 }
paulo@0 173
paulo@0 174 // If there is no hole in the bottom row, then 0 rows conform.
paulo@0 175 // This shouldn't happen in standard smg because the line would be
paulo@0 176 // cleared, but eventually LJ will support games other than SMG,
paulo@0 177 // and checking the array bounds is O(1), so why not?
paulo@0 178 if (hole >= p->rightWall) {
paulo@0 179 return 0;
paulo@0 180 }
paulo@0 181
paulo@0 182 // make sure that the row is clear through the whole visible
paulo@0 183 // portion of the playfield
paulo@0 184 for (unsigned int y = 0; y < p->ceiling; ++y) {
paulo@0 185
paulo@0 186 // If this column isn't empty, the whole field doesn't conform.
paulo@0 187 if (p->b[y][hole]) {
paulo@0 188 return 0;
paulo@0 189 }
paulo@0 190 }
paulo@0 191
paulo@0 192 for (unsigned int y = 0; y < p->ceiling; ++y) {
paulo@0 193
paulo@0 194 // At this point, the bottom y rows conform.
paulo@0 195 for (unsigned int x = p->leftWall; x < p->rightWall; ++x) {
paulo@0 196
paulo@0 197 // Disregarding the hole column, if there's an empty cell
paulo@0 198 // in this row, then this row does not conform.
paulo@0 199 if (x != hole && !p->b[y][x]) {
paulo@0 200 return y;
paulo@0 201 }
paulo@0 202 }
paulo@0 203 }
paulo@0 204 return p->ceiling;
paulo@0 205 }
paulo@0 206
paulo@0 207 /**
paulo@0 208 * Converts a rank number (0-19) to a text rank.
paulo@0 209 * 0-9: 10-1 kyu; 10-99: 1-90 dan; 100+: grandmaster
paulo@0 210 * @return number of characters written
paulo@0 211 */
paulo@0 212 static size_t printSecretGrade(char *dst, int rank) {
paulo@0 213 if (rank >= 100) {
paulo@0 214 return siprintf(dst, "GM");
paulo@0 215 } else if (rank >= 10) {
paulo@0 216 return siprintf(dst, "S%d", rank - 9);
paulo@0 217 } else {
paulo@0 218 return siprintf(dst, "%d", 10 - rank);
paulo@0 219 }
paulo@0 220 }
paulo@0 221
paulo@0 222 static size_t printFracG(char *dst, unsigned int value) {
paulo@0 223 if (value == 0) {
paulo@0 224 return siprintf(dst, "Instant");
paulo@0 225 } else if (value == 1) {
paulo@0 226 return siprintf(dst, "1G");
paulo@0 227 } else {
paulo@0 228 return siprintf(dst, "1/%dG", value);
paulo@0 229 }
paulo@0 230 }
paulo@0 231
paulo@0 232 #include <string.h> // remove once fourcc transition is complete
paulo@0 233 static size_t printFourCC(char *dst, FourCC f) {
paulo@0 234 const char *data = ljGetFourCCName(f);
paulo@0 235 if (data) {
paulo@0 236 int pos = 0;
paulo@0 237 for(pos = 0; *data; ++pos) {
paulo@0 238 dst[pos] = *data++;
paulo@0 239 }
paulo@0 240 return pos;
paulo@0 241 } else {
paulo@0 242 int pos = 0;
paulo@0 243 for(pos = 0; f.c[pos] && pos < 4; ++pos) {
paulo@0 244 dst[pos] = f.c[pos];
paulo@0 245 }
paulo@0 246 return pos;
paulo@0 247 }
paulo@0 248 }
paulo@0 249
paulo@0 250 size_t buildOptionsReportPage(char *dst, const LJView *v) {
paulo@0 251 size_t pos = 0;
paulo@0 252
paulo@0 253
paulo@0 254 #if USING_DEBRIEF_PAGE
paulo@0 255 const LJField *p = v->field;
paulo@0 256
paulo@0 257 pos += siprintf(dst + pos,
paulo@0 258 "Options\n\n"
paulo@0 259 "Well: %dx%d%s, Enter: %s\n"
paulo@0 260 "Speed: ",
paulo@0 261 p->rightWall - p->leftWall,
paulo@0 262 p->ceiling,
paulo@0 263 v->hidePF ? " invisible" : "",
paulo@0 264 p->enterAbove ? "Above" : "Below");
paulo@0 265 pos += printFourCC(dst + pos,
paulo@0 266 optionsSpeedCurveNames[(size_t)p->speedState.curve]);
paulo@0 267 pos += siprintf(dst + pos,
paulo@0 268 ", ARE: %d ms\n"
paulo@0 269 "Randomizer: ",
paulo@0 270 v->field->areStyle * 50 / 3);
paulo@0 271 pos += printFourCC(dst + pos,
paulo@0 272 optionsRandNames[(size_t)p->randomizer]);
paulo@0 273 pos += siprintf(dst + pos, " of ");
paulo@0 274 pos += printFourCC(dst + pos,
paulo@0 275 optionsPieceSetNames[(size_t)p->pieceSet]);
paulo@0 276 pos += siprintf(dst + pos, "\nHold: ");
paulo@0 277 pos += printFourCC(dst + pos,
paulo@0 278 optionsHoldStyleNames[(size_t)p->holdStyle]);
paulo@0 279 pos += siprintf(dst + pos, ", Rotation: ");
paulo@0 280 pos += printFourCC(dst + pos,
paulo@0 281 optionsRotNames[(size_t)p->rotationSystem]);
paulo@0 282 if (p->maxUpwardKicks < 20) {
paulo@0 283 pos += siprintf(dst + pos,
paulo@0 284 " %d FK",
paulo@0 285 (int)p->maxUpwardKicks);
paulo@0 286 }
paulo@0 287 if (v->control->initialRotate) {
paulo@0 288 pos += siprintf(dst + pos, "+initial");
paulo@0 289 }
paulo@0 290
paulo@0 291 pos += siprintf(dst + pos,
paulo@0 292 "\nLock: ");
paulo@0 293 if (p->setLockDelay >= 128) {
paulo@0 294 pos += siprintf(dst + pos, "Never");
paulo@0 295 } else {
paulo@0 296 if (p->setLockDelay > 0) {
paulo@0 297 pos += siprintf(dst + pos,
paulo@0 298 "%d ms ",
paulo@0 299 p->setLockDelay * 50 / 3);
paulo@0 300 }
paulo@0 301 pos += printFourCC(dst + pos,
paulo@0 302 optionsLockdownNames[(size_t)p->lockReset]);
paulo@0 303 }
paulo@0 304 pos += siprintf(dst + pos,
paulo@0 305 ", Deep: %s\n",
paulo@0 306 debriefBoolNames[p->bottomBlocks]);
paulo@0 307
paulo@0 308 pos += siprintf(dst + pos,
paulo@0 309 "Line clear: %d ms ",
paulo@0 310 p->speed.lineDelay * 50 / 3);
paulo@0 311 pos += printFourCC(dst + pos,
paulo@0 312 optionsGravNames[(size_t)p->clearGravity]);
paulo@0 313 pos += siprintf(dst + pos,
paulo@0 314 ", Gluing: ");
paulo@0 315 pos += printFourCC(dst + pos,
paulo@0 316 optionsGluingNames[(size_t)p->gluing]);
paulo@0 317 pos += siprintf(dst + pos,
paulo@0 318 "\nDrop score: ");
paulo@0 319 pos += printFourCC(dst + pos,
paulo@0 320 optionsDropScoringNames[(size_t)p->dropScoreStyle]);
paulo@0 321 pos += siprintf(dst + pos,
paulo@0 322 ", T-spin: ");
paulo@0 323 pos += printFourCC(dst + pos,
paulo@0 324 optionsTspinNames[(size_t)p->tSpinAlgo]);
paulo@0 325 pos += siprintf(dst + pos,
paulo@0 326 "\nGarbage: ");
paulo@0 327 pos += printFourCC(dst + pos,
paulo@0 328 optionsGarbageNames[(size_t)p->garbageStyle]);
paulo@0 329 pos += siprintf(dst + pos,
paulo@0 330 ", DAS: %d ms ",
paulo@0 331 v->control->dasDelay * 50 / 3);
paulo@0 332 pos += printFracG(dst + pos, v->control->dasSpeed);
paulo@0 333 pos += siprintf(dst + pos,
paulo@0 334 "\nSoft drop: ");
paulo@0 335 pos += printFracG(dst + pos, v->control->softDropSpeed + 1);
paulo@0 336 dst[pos++] = ' ';
paulo@0 337 pos += printFourCC(dst + pos,
paulo@0 338 optionsZangiNames[v->control->softDropLock]);
paulo@0 339 pos += siprintf(dst + pos,
paulo@0 340 ", Hard drop: ");
paulo@0 341 pos += printFourCC(dst + pos,
paulo@0 342 optionsZangiNames[v->control->hardDropLock]);
paulo@0 343 pos += siprintf(dst + pos, "\nShadow: ");
paulo@0 344 pos += printFourCC(dst + pos,
paulo@0 345 optionsShadowNames[v->hideShadow]);
paulo@0 346 pos += siprintf(dst + pos,
paulo@0 347 ", Next: %d\n",
paulo@0 348 v->nextPieces);
paulo@0 349 #else
paulo@0 350 pos += siprintf(dst, "\n\nDebrief disabled.\n");
paulo@0 351 #endif
paulo@0 352
paulo@0 353 dst[pos] = 0;
paulo@0 354 return pos;
paulo@0 355 }
paulo@0 356
paulo@0 357 #ifdef DEBRIEF_SHORT
paulo@0 358 static const char *const naiveGravity[8] = {
paulo@0 359 "1L", "2L", "3L", "4L",
paulo@0 360 "T1", "T2", "T3"
paulo@0 361 };
paulo@0 362 static const char *const cascadeGravity[8] = {
paulo@0 363 "1L", "2L", "3L", "4L",
paulo@0 364 "5L", "6L", "7L", "8L+"
paulo@0 365 };
paulo@0 366 #else
paulo@0 367 static const char *const naiveGravity[8] = {
paulo@0 368 "single", "double", "triple", "home run",
paulo@0 369 "T single", "T double", "T triple"
paulo@0 370 };
paulo@0 371 static const char *const cascadeGravity[8] = {
paulo@0 372 "single", "double", "triple", "quad",
paulo@0 373 "5L", "6L", "7L", "8L+"
paulo@0 374 };
paulo@0 375 #endif
paulo@0 376
paulo@0 377 static size_t printLineCounts(char *dst, const LJField *p) {
paulo@0 378 size_t pos = 0;
paulo@0 379 const char *const *names = (p->clearGravity == LJGRAV_NAIVE)
paulo@0 380 ? naiveGravity : cascadeGravity;
paulo@0 381 dst[pos++] = '(';
paulo@0 382 for (int i = 0; i < 8; ++i) {
paulo@0 383 if (names[i]) {
paulo@0 384 pos += siprintf(dst + pos,
paulo@0 385 "%s%s: %u",
paulo@0 386 i ? "; " : "",
paulo@0 387 names[i],
paulo@0 388 p->nLineClears[i]);
paulo@0 389 }
paulo@0 390 }
paulo@0 391 dst[pos++] = ')';
paulo@0 392 dst[pos++] = '\n';
paulo@0 393 return pos;
paulo@0 394 }
paulo@0 395
paulo@0 396
paulo@0 397 size_t buildDebriefPage(char *dst, const LJView *v) {
paulo@0 398 size_t pos = 0;
paulo@0 399
paulo@0 400 #if USING_DEBRIEF_PAGE
paulo@0 401 #ifndef NO_DATETIME
paulo@0 402 const time_t finishTimeUNIX = time(NULL);
paulo@0 403 const struct tm *finishTime = localtime(&finishTimeUNIX);
paulo@0 404 char finishTimeStr[64];
paulo@0 405
paulo@0 406 /* I would have used
paulo@0 407 strftime(finishTimeStr, sizeof(finishTimeStr), "%Y-%m-%d at %H:%M", finishTime);
paulo@0 408 but it brings in the floating-point library on GBA/DS. */
paulo@0 409 siprintf(finishTimeStr,
paulo@0 410 "%04d-%02d-%02d at %02d:%02d",
paulo@0 411 finishTime->tm_year + 1900,
paulo@0 412 finishTime->tm_mon + 1, finishTime->tm_mday,
paulo@0 413 finishTime->tm_hour, finishTime->tm_min);
paulo@0 414 #endif
paulo@0 415
paulo@0 416 const LJField *p = v->field;
paulo@0 417 unsigned long int keys_100m = p->nPieces
paulo@0 418 ? 100U * v->control->presses / p->nPieces
paulo@0 419 : 666;
paulo@0 420 TimeResultMSF gameMSF, activeMSF;
paulo@0 421 const char *wordPieces = (p->nPieces != 1) ? "tetrominoes" : "tetromino";
paulo@0 422 if (p->pieceSet == LJRAND_234BLK) {
paulo@0 423 wordPieces = (p->nPieces != 1) ? "pieces" : "piece";
paulo@0 424 }
paulo@0 425
paulo@0 426 /* Secret grades */
paulo@0 427 unsigned int nBlocksLeft = countBlocksLeft(p);
paulo@0 428 unsigned int nZigzagRows = calcZigzagGrade(p);
paulo@0 429 unsigned int nRectumRows = calcRectumGrade(p);
paulo@0 430
paulo@0 431 pos += siprintf(dst + pos,
paulo@0 432 "Result:\n\n%s ",
paulo@0 433 v->control->countdown <= 0 ? "Cleared" : "Stopped");
paulo@0 434 pos += printFourCC(dst + pos, gimmickNames[p->gimmick]);
paulo@0 435 pos += siprintf(dst + pos,
paulo@0 436 " at level %d\n",
paulo@0 437 p->speedState.level);
paulo@0 438 #if !defined(NO_DATETIME) || defined(WITH_REPLAY)
paulo@0 439 #ifndef NO_DATETIME
paulo@0 440 pos += siprintf(dst + pos, "on %s", finishTimeStr);
paulo@0 441 #endif
paulo@0 442 #ifdef WITH_REPLAY
paulo@0 443 if (p->reloaded) {
paulo@0 444 pos += siprintf(dst + pos, " from saved state");
paulo@0 445 }
paulo@0 446 #endif
paulo@0 447 dst[pos++] = '\n';
paulo@0 448 #endif
paulo@0 449
paulo@0 450 frames2msf(p->gameTime, p->nPieces, p->outGarbage, &gameMSF);
paulo@0 451 frames2msf(p->activeTime, p->nPieces, p->outGarbage, &activeMSF);
paulo@0 452
paulo@0 453 pos += siprintf(dst + pos,
paulo@0 454 "Played %d %s in %u:%02u.%02u (%u.%02u/min)\n",
paulo@0 455 p->nPieces,
paulo@0 456 wordPieces,
paulo@0 457 gameMSF.minutes,
paulo@0 458 (unsigned int)gameMSF.seconds,
paulo@0 459 gameMSF.frames * 5U / 3U,
paulo@0 460 (unsigned int) (gameMSF.pieces_100m / 100),
paulo@0 461 (unsigned int) (gameMSF.pieces_100m % 100));
paulo@0 462 pos += siprintf(dst + pos,
paulo@0 463 "(active time only: %u:%02u.%02u, %u.%02u/min)\n",
paulo@0 464 activeMSF.minutes,
paulo@0 465 (unsigned int)activeMSF.seconds,
paulo@0 466 activeMSF.frames * 5U / 3U,
paulo@0 467 (unsigned int) (activeMSF.pieces_100m / 100),
paulo@0 468 (unsigned int) (activeMSF.pieces_100m % 100));
paulo@0 469 pos += siprintf(dst + pos,
paulo@0 470 "Pressed %u keys (%u.%02u/piece)\n\n",
paulo@0 471 v->control->presses,
paulo@0 472 (unsigned int) (keys_100m / 100),
paulo@0 473 (unsigned int) (keys_100m % 100));
paulo@0 474
paulo@0 475 pos += siprintf(dst + pos,
paulo@0 476 "Made %u lines",
paulo@0 477 p->lines);
paulo@0 478 if (p->gluing == LJGLUING_SQUARE) {
paulo@0 479 pos += siprintf(dst + pos,
paulo@0 480 " and %u pure + %u squares",
paulo@0 481 p->monosquares, p->multisquares);
paulo@0 482 }
paulo@0 483 dst[pos++] = '\n';
paulo@0 484 pos += printLineCounts(dst + pos, p);
paulo@0 485 pos += siprintf(dst + pos,
paulo@0 486 "Sent %u garbage (%u.%02u per minute)\n",
paulo@0 487 p->outGarbage,
paulo@0 488 (unsigned int) (gameMSF.garbage_100m / 100),
paulo@0 489 (unsigned int) (gameMSF.garbage_100m % 100));
paulo@0 490 if (nZigzagRows >= MIN_ZIGZAG_ROWS) {
paulo@0 491 pos += siprintf(dst + pos,
paulo@0 492 "Made %u rows of > for grade ",
paulo@0 493 nZigzagRows);
paulo@0 494 pos += printSecretGrade(dst + pos,
paulo@0 495 nZigzagRows >= 19 ? 100 : nZigzagRows);
paulo@0 496 } else if (nRectumRows >= MIN_RECTUM_ROWS) {
paulo@0 497 pos += siprintf(dst + pos,
paulo@0 498 "Made %u-row rectum for grade ",
paulo@0 499 nRectumRows);
paulo@0 500 pos += printSecretGrade(dst + pos,
paulo@0 501 nRectumRows >= 20 ? 100 : nRectumRows - 1);
paulo@0 502 } else if (nBlocksLeft >= 110 && nBlocksLeft <= 111) {
paulo@0 503 pos += siprintf(dst + pos,
paulo@0 504 "Secret grade: Eleventy%s",
paulo@0 505 (nBlocksLeft & 1) ? "-one" : "");
paulo@0 506 } else {
paulo@0 507 pos += siprintf(dst + pos,
paulo@0 508 "Left %d blocks behind",
paulo@0 509 nBlocksLeft);
paulo@0 510 }
paulo@0 511 dst[pos++] = '\n';
paulo@0 512 dst[pos++] = '\n';
paulo@0 513 pos += printFourCC(dst + pos, optionsScoringNames[p->scoreStyle]);
paulo@0 514
paulo@0 515 pos += siprintf(dst + pos,
paulo@0 516 " score: %d\n",
paulo@0 517 p->score);
paulo@0 518
paulo@0 519 /*
paulo@0 520 tod stats not in lj
paulo@0 521
paulo@0 522 points/line, 40 lines score, silver squares, gold squares
paulo@0 523
paulo@0 524 */
paulo@0 525
paulo@0 526 #else
paulo@0 527 pos += siprintf(dst, "\n\nDebrief disabled. Score: %u\n", v->field->score);
paulo@0 528 #endif
paulo@0 529 return pos;
paulo@0 530 }
paulo@0 531
paulo@0 532 void debriefDrawPage(const char *page, size_t pageNumber);
paulo@0 533 LJBits debriefHandleKeys(void);
paulo@0 534 extern volatile char redrawWholeScreen;
paulo@0 535
paulo@0 536
paulo@0 537 /**
paulo@0 538 * Reports the player's performance.
paulo@0 539 */
paulo@0 540 void debrief(LJView *v) {
paulo@0 541 char pageData[2000]; // current length is under 1000 chars
paulo@0 542 char *page[2] = {pageData, "Page coming soon!"};
paulo@0 543 int curPage = 0;
paulo@0 544
paulo@0 545 {
paulo@0 546 size_t pos;
paulo@0 547
paulo@0 548 pos = buildDebriefPage(page[0], v);
paulo@0 549 page[1] = page[0] + (++pos);
paulo@0 550 pos += buildOptionsReportPage(page[1], v);
paulo@0 551 }
paulo@0 552 #ifdef HAS_FOPEN
paulo@0 553 FILE *logFile = ljfopen("lj-scores.txt", "at");
paulo@0 554 if (logFile) {
paulo@0 555 fputs("\n\n\n", logFile);
paulo@0 556 fputs(page[0], logFile);
paulo@0 557 fputs(page[1], logFile);
paulo@0 558 #ifdef DEBRIEF_TO_STDOUT
paulo@0 559 fputs(page[0], stdout);
paulo@0 560 fputs(page[1], stdout);
paulo@0 561 #endif
paulo@0 562 fclose(logFile);
paulo@0 563 }
paulo@0 564 #endif
paulo@0 565
paulo@0 566 LJBits lastKeys = ~0;
paulo@0 567 redrawWholeScreen = 1;
paulo@0 568 debriefHandleKeys(); // call once to clear key buffer
paulo@0 569
paulo@0 570 for (;;) {
paulo@0 571 LJBits sounds = 0;
paulo@0 572
paulo@0 573 if (redrawWholeScreen) {
paulo@0 574 redrawWholeScreen = 0;
paulo@0 575 debriefDrawPage(page[curPage], curPage);
paulo@0 576 }
paulo@0 577 LJBits keys = debriefHandleKeys();
paulo@0 578 LJBits newKeys = keys & ~lastKeys;
paulo@0 579
paulo@0 580 if (newKeys & VKEY_LEFT) {
paulo@0 581 if (curPage > 0) {
paulo@0 582 curPage -= 1;
paulo@0 583 redrawWholeScreen = 1;
paulo@0 584 sounds |= LJSND_HOLD;
paulo@0 585 }
paulo@0 586 }
paulo@0 587
paulo@0 588 if (newKeys & VKEY_RIGHT) {
paulo@0 589 if (curPage + 1 < sizeof(page) / sizeof(page[0])) {
paulo@0 590 curPage += 1;
paulo@0 591 redrawWholeScreen = 1;
paulo@0 592 sounds |= LJSND_HOLD;
paulo@0 593 }
paulo@0 594 }
paulo@0 595
paulo@0 596 if (newKeys & (VKEY_ROTR | VKEY_ROTL)) {
paulo@0 597 break;
paulo@0 598 } else {
paulo@0 599 lastKeys = keys;
paulo@0 600 }
paulo@0 601 playSoundEffects(v, sounds, 100);
paulo@0 602 }
paulo@0 603 }