#include <stdio.h>
#include <stdlib.h>

// This reads the large file of generated Unblockme puzzles generated by "allgames.c",  and allows us to
// output a selection in a much compressed format for use in Unblockme games on platforms with restricted
// code/data space (i.e. Eprom) such as the Vectrex.  The contents of the input file look like this:

// 1 "AAABB.CCLDDDEELXXNFFFGGNHHMII.JJMKKK" # NX  0% done.  VState=21/22 21/22 10/12 21/22 21/22 18/22  HState=0/22 4/22 3/12 0/22 7/22 4/22

// (and the current input file contains 14,345,851 entries!)

// Compile using cc -O -o gameno gameno.c
// and invoke with: ./gameno < all-unblockme-games.txt

// A smaller input file sorted by order of difficulty can be generated by a command such as:
//
//  grep ^[3-9][0-9] all-unblockme-games.txt|sort -nr
//

#define LOWEST_SCORE 30  // set to 0 for all entries.

int main(int argc, char **argv) {
  int moves, percent_done;
  int voption1, voption2, voption3, voption4, voption5, voption6,
       option1,  option2,  option3,  option4,  option5,  option6;
  char layout[6*6+1], solution[128+1];
  int line = 0, games = 0;
  
  fprintf(stdout, "#include <stdio.h>\n");
  fprintf(stdout, "#include <stdint.h>\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "// Solved in 34 moves: ./solve \"AABBB...ICCCXXIK..HDDKEEH.JFFLGGJ..L\" # BiXhhdjGGGJDHHxHdIIccckelllEFGKCCKKeLCiiXXXIbIcllX  14%% done.  VState=19/22 21/22 10/12 18/22 21/22 20/22  HState=3/22 15/22 8/12 10/22 19/22 16/22\n");
  fprintf(stdout, "// is represented by 0x27554ab4 0x06f42a70\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "// When enumerating potential board positions, only a few options are possible\n");
  fprintf(stdout, "// on each empty row, being constrained by the block length and game rules.\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "// Allowed forms for columns, and all rows except exit row:\n");
  fprintf(stdout, "const char *options12456[] = {\n");
  fprintf(stdout, "  \"AAABB.\", \"AAA.BB\", \".AAABB\", \"AABBB.\", \"AA.BBB\", \".AABBB\", \"AABB..\",\n");
  fprintf(stdout, "  \"AA.BB.\", \"AA..BB\", \".AABB.\", \".AA.BB\", \"..AABB\", \"AAA...\", \".AAA..\",\n");
  fprintf(stdout, "  \"..AAA.\", \"...AAA\", \"AA....\", \".AA...\", \"..AA..\", \"...AA.\", \"....AA\",\n");
  fprintf(stdout, "  \"......\",\n");
  fprintf(stdout, "};\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "// The exit row (3) is constrained.  The block to be extracted cannot have any horizontal blocks to the right of it,\n");
  fprintf(stdout, "// and it must have at least one column free to the right of it to be obstructed by a vertical block.\n");
  fprintf(stdout, "const char *options3[] = {\n");
  fprintf(stdout, "  \"AAAXX.\",  \"AAXXX.\",  \"AAXX..\",  \"AA.XX.\",  \".AAXX.\",  \"XXX...\",\n");
  fprintf(stdout, "  \".XXX..\",  \"..XXX.\",  \"XX....\",  \".XX...\",  \"..XX..\",  \"...XX.\",\n");
  fprintf(stdout, "};\n");
  fprintf(stdout, "\n");

  fprintf(stdout, "int32_t layout[] = { // Compressed layouts: 8 bytes per board\n");
  fprintf(stdout, "                     // 5 spare bits can store 0:31 if needed\n");
  for (;;) {
    int rc;
    if (feof(stdin)) break;
    rc = fscanf(stdin,
                "%d "
                "\"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\""
                " # %s  %d%% done.  "
                "VState=%d/22 %d/22 %d/12 %d/22 %d/22 %d/22  "
                "HState=%d/22 %d/22 %d/12 %d/22 %d/22 %d/22\n\n",
                &moves,
                &layout[ 0], &layout[ 1], &layout[ 2], &layout[ 3], &layout[ 4], &layout[ 5],  
                &layout[ 6], &layout[ 7], &layout[ 8], &layout[ 9], &layout[10], &layout[11],  
                &layout[12], &layout[13], &layout[14], &layout[15], &layout[16], &layout[17],  
                &layout[18], &layout[19], &layout[20], &layout[21], &layout[22], &layout[23],  
                &layout[24], &layout[25], &layout[26], &layout[27], &layout[28], &layout[29],  
                &layout[30], &layout[31], &layout[32], &layout[33], &layout[34], &layout[35],  
                solution,
                &percent_done,
                &voption1, &voption2, &voption3, &voption4, &voption5, &voption6,
                 &option1,  &option2,  &option3,  &option4,  &option5,  &option6);
    layout[6*6] = '\0';
    if (rc == 51) {
      line += 2;
      if (moves >= LOWEST_SCORE) {
        fprintf(stdout, "  0x%08x, 0x%08x, // \"%s\" \"%s\" %d moves.\n",
                (((((((((voption1<<5)|voption2)<<5)|voption3)<<5)|voption4)<<5)|voption5)<<5)|voption6,
                ((((((((( option1<<5)| option2)<<5)| option3)<<5)| option4)<<5)| option5)<<5)| option6,
                layout, solution, moves
                );
        games += 1;
        //if (games == 10) break;
      }
    } else {
      fprintf(stdout, "rc = %d at line %d\n", rc, line);
      exit(EXIT_FAILURE);
    }
  }
  fprintf(stdout, "  0, 0\n};\n\n");
  fprintf(stdout, "#define NUM_PUZZLES ((sizeof(layout)/sizeof(layout[0]))/2-1)\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "#ifdef DECOMPRESS_MAIN\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "int assign_horizontal_row(const char *row, char *dest, const int base_letter, int *empties) {\n");
  fprintf(stdout, "  int pos, c, letters_placed = 0, blocks_placed = 0;\n");
  fprintf(stdout, "  for (pos = 0; pos < 6; pos++) {\n");
  fprintf(stdout, "    c = row[pos];\n");
  fprintf(stdout, "    if (c == '.') {\n");
  fprintf(stdout, "      (*empties) += 1;\n");
  fprintf(stdout, "    } else if (c != 'X') {\n");
  fprintf(stdout, "      c = c -'A' + base_letter;\n");
  fprintf(stdout, "      blocks_placed += 1;\n");
  fprintf(stdout, "    }\n");
  fprintf(stdout, "    dest[pos] = c; // assign empties as well as blocks\n");
  fprintf(stdout, "  }\n");
  fprintf(stdout, "                                              // 0\n");
  fprintf(stdout, "  if (blocks_placed != 0) letters_placed = 1; // 1\n");
  fprintf(stdout, "  if (blocks_placed > 3) letters_placed += 1; // 2\n");
  fprintf(stdout, "  return letters_placed;\n");
  fprintf(stdout, "}\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "int assign_vertical_column(const char *col, char *dest, const int base_letter, int *empties) {\n");
  fprintf(stdout, "  int pos, c, letters_placed = 0, blocks_placed = 0, destpos = 0;\n");
  fprintf(stdout, "  for (pos = 0; pos < 6; pos++) {\n");
  fprintf(stdout, "    c = col[pos];\n");
  fprintf(stdout, "    if (c != '.') { // No 'X' to worry about vertically...\n");
  fprintf(stdout, "      // and we don't need to check the destination as it was already validated by 'vertically_compatible()'\n");
  fprintf(stdout, "      // only assign blocks, not empty spaces.\n");
  fprintf(stdout, "      dest[destpos] = c -'A' + base_letter;\n");
  fprintf(stdout, "      blocks_placed += 1;\n");
  fprintf(stdout, "      (*empties) -= 1;\n");
  fprintf(stdout, "    }\n");
  fprintf(stdout, "    destpos += 6;\n");
  fprintf(stdout, "  }\n");
  fprintf(stdout, "                                              // 0\n");
  fprintf(stdout, "  if (blocks_placed != 0) letters_placed = 1; // 1\n");
  fprintf(stdout, "  if (blocks_placed > 3) letters_placed += 1; // 2\n");
  fprintf(stdout, "  return letters_placed;\n");
  fprintf(stdout, "}\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "#define a ( sizeof(options12456)/sizeof(options12456[0]) )         // number of options to fill a regular row or a column\n");
  fprintf(stdout, "#define b ( sizeof(options3)    /sizeof(options3[0])     )         // number of options allowed in the exit row.\n");
  fprintf(stdout, "  \n");
  fprintf(stdout, "\n");
  fprintf(stdout, "// These define the state of the generator, containing the current choice of tiles for each row and column at any time.\n");
  fprintf(stdout, "// They are printed in the log file and can be used to resume an abandoned run (which is a feature that was added because\n");
  fprintf(stdout, "// currently a run can take a few hours).  'volatile', so that the data is up to date when saving on a trapped ^C.\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "// (Note that for efficiency we actually restart an abandoned run using the last row positions only - the column\n");
  fprintf(stdout, "// positions are refilled from scratch.)\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "static int voption1, voption2, voption3, voption4, voption5, voption6,\n");
  fprintf(stdout, "            option1,  option2,  option3,  option4,  option5,  option6;\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "int main(int argc, char **argv) {\n");
  fprintf(stdout, "  int v, h, vv, hh, empties = 0, base_letter = 'A';\n");
  fprintf(stdout, "  char board[6*6+1];\n");
  fprintf(stdout, "  \n");
  fprintf(stdout, "  fprintf(stderr, \"Please enter two integers separated by spaces, for example: 0x27554ab4 0x06f42a70\\n\");\n");
  fprintf(stdout, "  fscanf(stdin, \"%%x %%x\", &v, &h);\n");
  fprintf(stdout, "  vv = v; hh = h;\n");
  fprintf(stdout, "  \n");
  fprintf(stdout, "  voption6 = v & 31; v = v>>5;\n");
  fprintf(stdout, "  voption5 = v & 31; v = v>>5;\n");
  fprintf(stdout, "  voption4 = v & 31; v = v>>5;\n");
  fprintf(stdout, "  voption3 = v & 31; v = v>>5;\n");
  fprintf(stdout, "  voption2 = v & 31; v = v>>5;\n");
  fprintf(stdout, "  voption1 = v & 31; v = v>>5;\n");
  fprintf(stdout, "  // remaining v contains 2 spare bits.\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "  option6 = h & 31; h = h>>5;\n");
  fprintf(stdout, "  option5 = h & 31; h = h>>5;\n");
  fprintf(stdout, "  option4 = h & 31; h = h>>5;\n");
  fprintf(stdout, "  // h&16 contains one of the spare bits if we ever use it.\n");
  fprintf(stdout, "  option3 = h & 15; h = h>>5;\n");
  fprintf(stdout, "  option2 = h & 31; h = h>>5;\n");
  fprintf(stdout, "  option1 = h & 31; h = h>>5;\n");
  fprintf(stdout, "  // remaining h contains the remaining 2 spare bits.\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "  base_letter += assign_horizontal_row(options12456[option1], &board[0], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_horizontal_row(options12456[option2], &board[6], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_horizontal_row(options3    [option3], &board[12], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_horizontal_row(options12456[option4], &board[18], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_horizontal_row(options12456[option5], &board[24], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_horizontal_row(options12456[option6], &board[30], base_letter, &empties);\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "  base_letter += assign_vertical_column(options12456[voption1], &board[0], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_vertical_column(options12456[voption2], &board[1], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_vertical_column(options12456[voption3], &board[2], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_vertical_column(options12456[voption4], &board[3], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_vertical_column(options12456[voption5], &board[4], base_letter, &empties);\n");
  fprintf(stdout, "  base_letter += assign_vertical_column(options12456[voption6], &board[5], base_letter, &empties);\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "  board[6*6] = '\\0';\n");
  fprintf(stdout, "  fprintf(stdout, \"The board encoded as 0x%%08x 0x%%08x is \\\"%%s\\\"  #  \", vv, hh, board);\n");
  fprintf(stdout, "  fprintf(stdout,\n");
  fprintf(stdout, "          \"unpacked as \"\n");
  fprintf(stdout, "          \"V: %%d/22 %%d/22 %%d/22 %%d/22 %%d/22 %%d/22    \"\n");
  fprintf(stdout, "          \"H: %%d/22 %%d/22 %%d/12 %%d/22 %%d/22 %%d/22\\n\\n\",\n");
  fprintf(stdout, "          voption1, voption2, voption3, voption4, voption5, voption6,\n");
  fprintf(stdout, "           option1,  option2,  option3,  option4,  option5,  option6);\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "}\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "#endif\n");

  exit(EXIT_SUCCESS);
  return EXIT_FAILURE;
}
