Advertisement
Guest User

Untitled

a guest
Dec 22nd, 2014
142
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.53 KB | None | 0 0
  1. #include <cstdio>
  2. #include <vector>
  3. #include <cstdint>
  4. #include "stb_image.h"
  5. #include <unordered_set>
  6. #include <algorithm>
  7. #include <cassert>
  8. #include <chrono>
  9.  
  10. enum Direction
  11. {
  12.     Up,
  13.     Down,
  14.     Left,
  15.     Right,
  16. };
  17.  
  18. struct Point
  19. {
  20.     int x, y;
  21.  
  22. };
  23.  
  24. bool operator==(const Point& a, const Point& b)
  25. {
  26.     return a.x == b.x && a.y == b.y;
  27. }
  28. bool operator!=(const Point& a, const Point& b)
  29. {
  30.     return !(a == b);
  31. }
  32.  
  33. Point get_offset(int x, int y, Direction dir)
  34. {
  35.     switch (dir)
  36.     {
  37.     case Up: return { x, y - 1 };
  38.     case Down: return { x, y + 1 };
  39.     case Left: return { x - 1, y };
  40.     case Right: return { x + 1, y };
  41.     }
  42.  
  43.     return { x, y };
  44. }
  45.  
  46. Direction get_opposite(Direction dir)
  47. {
  48.     switch (dir)
  49.     {
  50.     case Up: return Down;
  51.     case Down: return Up;
  52.     case Left: return Right;
  53.     case Right: return Left;
  54.     }
  55.     return Up;
  56. }
  57.  
  58. struct Freedoms
  59. {
  60.     Direction directions[4];
  61.     uint16_t count;
  62. };
  63.  
  64. struct Board
  65. {
  66.     enum EdgeState
  67.     {
  68.         EdgeEmpty = 0,
  69.         EdgeWall = 1 << 0,
  70.         EdgeLine = 1 << 1,
  71.         EdgeDoor = 1 << 2,
  72.     };
  73.  
  74.     enum CellState
  75.     {
  76.         CellEmpty,
  77.         CellFullLine,
  78.         CellHalfLine,
  79.     };
  80.  
  81.     std::vector<EdgeState> horizontal_edges;
  82.     std::vector<EdgeState> vertical_edges;
  83.     std::vector<CellState> cell_states;
  84.  
  85.     bool half_finished = false;
  86.     bool finished = false;
  87.     Point half_finish_point;
  88.     Point finish_point;
  89.  
  90.     EdgeState get_edge(int x, int y, Direction dir)
  91.     {
  92.         switch (dir)
  93.         {
  94.         case Up:
  95.             return horizontal_edges[x + y * width];
  96.         case Down:
  97.             return horizontal_edges[x + (y + 1) * width];
  98.         case Left:
  99.             return vertical_edges[x + y * (width + 1)];
  100.         case Right:
  101.             return vertical_edges[x + 1 + y * (width + 1)];
  102.         }
  103.         return EdgeWall;
  104.     }
  105.     void set_edge(int x, int y, Direction dir, EdgeState edge)
  106.     {
  107.         switch (dir)
  108.         {
  109.         case Up:
  110.             horizontal_edges[x + y * width] = edge;
  111.             break;
  112.         case Down:
  113.             horizontal_edges[x + (y + 1) * width] = edge;
  114.             break;
  115.         case Left:
  116.             vertical_edges[x + y * (width + 1)] = edge;
  117.             break;
  118.         case Right:
  119.             vertical_edges[x + 1 + y * (width + 1)] = edge;
  120.             break;
  121.         }
  122.     }
  123.  
  124.     CellState get_cell(int x, int y)
  125.     {
  126.         int linecount = 0;
  127.         for (int i = 0; i < 4; i++)
  128.         {
  129.             EdgeState edge = get_edge(x, y, (Direction)i);
  130.             if (edge == EdgeLine)
  131.                 linecount++;
  132.         }
  133.         switch (linecount)
  134.         {
  135.         case 0: return CellEmpty;
  136.         case 1: return CellHalfLine;
  137.         case 2: return CellFullLine;
  138.         }
  139.         return CellEmpty;
  140.     }
  141.  
  142.     CellState get_neighbor_cell(int x, int y, Direction dir)
  143.     {
  144.         switch (dir)
  145.         {
  146.         case Up:
  147.             return y > 0 ? get_cell(x, y - 1) : CellEmpty;
  148.         case Down:
  149.             return y + 1 < height ? get_cell(x, y + 1) : CellEmpty;
  150.         case Left:
  151.             return x > 0 ? get_cell(x - 1, y) : CellEmpty;
  152.         case Right:
  153.             return x + 1 < width ? get_cell(x + 1, y) : CellEmpty;
  154.         }
  155.         return CellEmpty;
  156.     }
  157.    
  158.     int width, height;
  159. };
  160.  
  161. bool operator==(const Board& a, const Board& b)
  162. {
  163.     return a.horizontal_edges == b.horizontal_edges && a.vertical_edges == b.vertical_edges;
  164. }
  165.  
  166. struct BoardHash
  167. {
  168.     size_t operator()(const Board& b)
  169.     {
  170.         size_t hash = 0;
  171.         for (unsigned i = 0; i < b.vertical_edges.size(); i++)
  172.             hash ^= b.vertical_edges[i] + 0x9e3779b9 + (hash << 6) + (hash >> 2);
  173.         for (unsigned i = 0; i < b.horizontal_edges.size(); i++)
  174.             hash ^= b.horizontal_edges[i] + 0x9e3779b9 + (hash << 6) + (hash >> 2);
  175.         return hash;
  176.     }
  177. };
  178.  
  179. Freedoms get_freedoms(Board& board, int x, int y)
  180. {
  181.     Freedoms freedoms = { };
  182.     for (int i = 0; i < 4; i++)
  183.     {
  184.         Direction dir = (Direction)i;
  185.  
  186.         Board::EdgeState edge = board.get_edge(x, y, dir);
  187.         if (edge != Board::EdgeEmpty && edge != Board::EdgeDoor)
  188.             continue;
  189.  
  190.         Board::CellState cell = board.get_neighbor_cell(x, y, dir);
  191.         if (cell == Board::CellFullLine)
  192.             continue;
  193.  
  194.         freedoms.directions[freedoms.count++] = dir;
  195.     }
  196.     return freedoms;
  197. }
  198.  
  199. enum SolveState
  200. {
  201.     SolveUnchanged = 0,
  202.     SolveChanged = 1,
  203.     SolveInvalid = 2,
  204. };
  205.  
  206. enum SolveResult
  207. {
  208.     ResultSolved,
  209.     ResultInvalid,
  210.     ResultIndeterminate,
  211. };
  212.  
  213. // Returns true if no cycles were found
  214. bool follow_line(Board& board, int x, int y, Point* out = nullptr, int* outLen = nullptr, int last=-1)
  215. {
  216.     int cx = x, cy = y;
  217.     int len = 0;
  218.     while (true)
  219.     {
  220.         int i;
  221.         for (i = 0; i < 4; i++)
  222.         {
  223.             if (i == last)
  224.                 continue;
  225.  
  226.             if (board.get_edge(cx, cy, (Direction)i) == Board::EdgeLine)
  227.             {
  228.                 Point nextPoint = get_offset(cx, cy, (Direction)i);
  229.                 cx = nextPoint.x;
  230.                 cy = nextPoint.y;
  231.                 last = get_opposite((Direction)i);
  232.                 len++;
  233.                 break;
  234.             }
  235.         }
  236.  
  237.         if (i == 4 || cx < 0 || cy < 0 || cx >= board.width || cy >= board.height)
  238.         {
  239.             if (out)
  240.             {
  241.                 out->x = cx;
  242.                 out->y = cy;
  243.             }
  244.             if (outLen)
  245.                 *outLen = len;
  246.             return true;
  247.         }
  248.  
  249.         if (x == cx && y == cy)
  250.             return false;
  251.     }
  252. }
  253.  
  254. bool follow_line_on_edge(Board& board, int x, int y, Point* out, int* outLen)
  255. {
  256.     if (x < 0)
  257.         return follow_line(board, x + 1, y, out, outLen, Left);
  258.     if (y < 0)
  259.         return follow_line(board, x, y + 1, out, outLen, Up);
  260.     if (x >= board.width)
  261.         return follow_line(board, x - 1, y, out, outLen, Right);
  262.     if (y >= board.height)
  263.         return follow_line(board, x, y - 1, out, outLen, Down);
  264.     return false;
  265. }
  266.  
  267. SolveState solve_one_freedom(Board& board)
  268. {
  269.     bool didSomething = false;
  270.     for (int x = 0; x < board.width; x++)
  271.     {
  272.         for (int y = 0; y < board.height; y++)
  273.         {
  274.             Board::CellState cell = board.get_cell(x, y);
  275.  
  276.             if (cell != Board::CellHalfLine)
  277.                 continue;
  278.                
  279.             Freedoms freedoms = get_freedoms(board, x, y);
  280.             if (freedoms.count == 1)
  281.             {
  282.                 board.set_edge(x, y, freedoms.directions[0], Board::EdgeLine);
  283.                 didSomething = true;
  284.                 if (!follow_line(board, x, y))
  285.                     return SolveInvalid;
  286.             }
  287.             else if (freedoms.count == 2)
  288.             {
  289.                 for (int i = 0; i < 2; i++)
  290.                 {
  291.                     bool isInvalid = false;
  292.  
  293.                     Board::CellState ncell = board.get_neighbor_cell(x, y, freedoms.directions[i]);
  294.                     if (ncell == Board::CellHalfLine)
  295.                     {
  296.  
  297.                         Point pos = get_offset(x, y, freedoms.directions[i]);
  298.                         Point outPoint;
  299.                         if (!follow_line(board, pos.x, pos.y, &outPoint))
  300.                             return SolveInvalid;
  301.                         if (outPoint.x == x && outPoint.y == y)
  302.                         {
  303.                             isInvalid = true;
  304.                         }
  305.                     }
  306.  
  307.                     Board::EdgeState nedge = board.get_edge(x, y, freedoms.directions[i]);
  308.                     if (nedge == Board::EdgeDoor)
  309.                     {
  310.                         if (board.half_finished)
  311.                         {
  312.                             isInvalid = true;
  313.                         }
  314.                     }
  315.  
  316.                     if (isInvalid)
  317.                     {
  318.                         board.set_edge(x, y, freedoms.directions[(i + 1) % 2], Board::EdgeLine);
  319.                         didSomething = true;
  320.                         break;
  321.                     }
  322.                 }
  323.             }
  324.             else if (freedoms.count == 0)
  325.                 return SolveInvalid;
  326.         }
  327.     }
  328.     return didSomething ? SolveChanged : SolveUnchanged;
  329. }
  330.  
  331. SolveState solve_two_freedoms(Board& board)
  332. {
  333.     bool didSomething = false;
  334.     for (int x = 0; x < board.width; x++)
  335.     {
  336.         for (int y = 0; y < board.height; y++)
  337.         {
  338.             Board::CellState cell = board.get_cell(x, y);
  339.  
  340.             if (cell == Board::CellFullLine || cell == Board::CellHalfLine)
  341.                 continue;
  342.                
  343.             Freedoms freedoms = get_freedoms(board, x, y);
  344.  
  345.             if (freedoms.count == 0)
  346.             {
  347.                 return SolveInvalid;
  348.             }
  349.  
  350.             if (freedoms.count == 2)
  351.             {
  352.                 board.set_edge(x, y, freedoms.directions[0], Board::EdgeLine);
  353.                 board.set_edge(x, y, freedoms.directions[1], Board::EdgeLine);
  354.  
  355.                 if (!follow_line(board, x, y))
  356.                     return SolveInvalid;
  357.  
  358.                 didSomething = true;
  359.             }
  360.         }
  361.     }
  362.     return didSomething ? SolveChanged : SolveUnchanged;
  363. }
  364.  
  365. SolveState solve_bouncing(Board& board)
  366. {
  367.     return SolveUnchanged;
  368. }
  369.  
  370. SolveState solve_blocked_exits(Board& board)
  371. {
  372.     int linecount = 0;
  373.     int doorcount = 0;
  374.  
  375.     struct EdgePos
  376.     {
  377.         int x, y;
  378.         Direction dir;
  379.     };
  380.  
  381.     EdgePos doors[2];
  382.     Point linePos[2];
  383.  
  384.     auto process_edge = [&](int x, int y, Direction dir){
  385.         Board::EdgeState edge = board.get_edge(x, y, dir);
  386.         if (edge == Board::EdgeLine)
  387.         {
  388.             if (linecount < 2)
  389.                 linePos[linecount] = get_offset(x, y, dir);
  390.             linecount++;
  391.         }
  392.         else if (edge == Board::EdgeDoor)
  393.         {
  394.             if (board.get_cell(x, y) == Board::CellFullLine)
  395.                 return;
  396.  
  397.             if (doorcount < 2)
  398.                 doors[doorcount++] = { x, y, dir };
  399.             else
  400.                 doorcount++;
  401.         }
  402.     };
  403.  
  404.     for (int x = 0; x < board.width; x++)
  405.     {
  406.         process_edge(x, 0, Up);
  407.         process_edge(x, board.height - 1, Down);
  408.     }
  409.     for (int y = 0; y < board.height; y++)
  410.     {
  411.         process_edge(0, y, Left);
  412.         process_edge(board.width - 1, y, Right);
  413.     }
  414.  
  415.     if (linecount > 2 || linecount + doorcount < 2)
  416.         return SolveInvalid;
  417.  
  418.     bool didSomething = false;
  419.     if (linecount >= 1 && !board.half_finished)
  420.     {
  421.         didSomething = true;
  422.         board.half_finished = true;
  423.         board.half_finish_point = linePos[0];
  424.     }
  425.  
  426.     if (linecount == 2 && !board.finished)
  427.     {
  428.         didSomething = true;
  429.         board.finished = true;
  430.         board.finish_point = linePos[1];
  431.     }
  432.  
  433.     if (doorcount > 0 && doorcount + linecount <= 2)
  434.     {
  435.         for (int i = 0; i < doorcount; i++)
  436.         {
  437.             board.set_edge(doors[i].x, doors[i].y, doors[i].dir, Board::EdgeLine);
  438.         }
  439.         didSomething = true;
  440.     }
  441.  
  442.     return didSomething ? SolveChanged : SolveUnchanged;
  443. }
  444.  
  445. SolveResult is_solved(Board& board)
  446. {
  447.     if (!board.half_finished || !board.finished)
  448.         return ResultIndeterminate;
  449.  
  450.     int neededLength = board.width * board.height;
  451.  
  452.     int length;
  453.     Point hfp = board.half_finish_point;
  454.     Point out;
  455.     if (!follow_line_on_edge(board, hfp.x, hfp.y, &out, &length))
  456.         return ResultInvalid;
  457.  
  458.     if (out != board.finish_point)
  459.         return ResultIndeterminate;
  460.  
  461.     if (length != neededLength)
  462.         return ResultInvalid;
  463.  
  464.     return ResultSolved;
  465. }
  466.  
  467. const char *wallChars =
  468. // 00, 0U, D0, UD
  469. "\x20\xBA\xBA\xBA" // 00
  470. "\xCD\xBC\xBB\xB9" // 0L
  471. "\xCD\xC8\xC9\xCC" // R0
  472. "\xCD\xCA\xCB\xCE" // RL
  473. ;
  474.  
  475. const char *lineChars =
  476. // 00, 0U, D0, UD
  477. "\x2E\x2E\x2E\xB3" // 00
  478. "\x2E\xD9\xBF\xB4" // 0L
  479. "\x2E\xC0\xDA\xC3" // R0
  480. "\xC4\xC1\xC2\xC5" // RL
  481. ;
  482.  
  483. const char *horizontalEdgeChars =
  484. // Empty, wall, line, door
  485. "\x20\xCD\xB3\x20"
  486. ;
  487. const char *verticalEdgeChars =
  488. // Empty, wall, line, door
  489. "\x20\xBA\xC4\x20"
  490. ;
  491.  
  492. void draw_board(Board& board)
  493. {
  494.     for (int y = 0; y < board.height + 1; y++)
  495.     {
  496.         for (int x = 0; x < board.width + 1; x++)
  497.         {
  498.             int wallmask = 0;
  499.             int wedge = Board::EdgeWall | Board::EdgeDoor;
  500.             int hedge = Board::EdgeWall | Board::EdgeDoor;
  501.  
  502.             if (x == 0 || x == board.width)
  503.                 wedge |= Board::EdgeLine;
  504.             if (y == 0 || y == board.height)
  505.                 hedge |= Board::EdgeLine;
  506.  
  507.  
  508.             wallmask |= y > 0 && board.get_edge(x, y - 1, Left) & wedge ? 1 : 0;
  509.             wallmask |= y < board.height && board.get_edge(x, y, Left) & wedge ? 2 : 0;
  510.             wallmask |= x > 0 && board.get_edge(x - 1, y, Up) & hedge ? 4 : 0;
  511.             wallmask |= x < board.width && board.get_edge(x, y, Up) & hedge ? 8 : 0;
  512.  
  513.             putchar(wallChars[wallmask]);
  514.  
  515.             if (x < board.width)
  516.                 putchar(horizontalEdgeChars[board.get_edge(x, y, Up)]);
  517.         }
  518.  
  519.         putchar('\n');
  520.  
  521.         if (y < board.height)
  522.         {
  523.             for (int x = 0; x < board.width + 1; x++)
  524.             {
  525.                 putchar(verticalEdgeChars[board.get_edge(x, y, Left)]);
  526.  
  527.                 if (x < board.width)
  528.                 {
  529.                     int linemask = 0;
  530.                     int edge = Board::EdgeLine;
  531.  
  532.                     linemask |= board.get_edge(x, y, Up) & edge ? 1 : 0;
  533.                     linemask |= board.get_edge(x, y, Down) & edge ? 2 : 0;
  534.                     linemask |= board.get_edge(x, y, Left) & edge ? 4 : 0;
  535.                     linemask |= board.get_edge(x, y, Right) & edge ? 8 : 0;
  536.  
  537.                     putchar(lineChars[linemask]);
  538.                 }
  539.             }
  540.  
  541.             putchar('\n');
  542.         }
  543.     }
  544. }
  545.  
  546. SolveResult deterministic_solve_board(Board& board)
  547. {
  548.     int state;
  549.     do
  550.     {
  551.         state = SolveUnchanged;
  552.  
  553.         state |= solve_blocked_exits(board);
  554.         state |= solve_two_freedoms(board);
  555.         state |= solve_one_freedom(board);
  556.         state |= solve_bouncing(board);
  557.  
  558.         if (state & SolveInvalid)
  559.             return ResultInvalid;
  560.     }
  561.     while (state & SolveChanged);
  562.  
  563.     return is_solved(board);
  564. }
  565.  
  566. SolveResult single_option_solve_board(Board& board)
  567. {
  568.     struct CellLine
  569.     {
  570.         Direction a, b;
  571.     };
  572.  
  573.     static const CellLine cellLines[] = {
  574.         { Up, Down },
  575.         { Left, Right },
  576.         { Up, Left },
  577.         { Up, Right },
  578.         { Down, Left },
  579.         { Down, Right },
  580.     };
  581.  
  582.     bool didSomething;
  583.     do
  584.     {
  585.         didSomething = false;
  586.         for (int y = 0; y < board.height; y++)
  587.         {
  588.             for (int x = 0; x < board.width; x++)
  589.             {
  590.                 Board::CellState cell = board.get_cell(x, y);
  591.  
  592.                 if (cell == Board::CellEmpty)
  593.                 {
  594.                     int possible = 0;
  595.                     int lastSolve = -1;
  596.                     for (int i = 0; i < 6; i++)
  597.                     {
  598.                         CellLine lineType = cellLines[i];
  599.                         if (board.get_edge(x, y, lineType.a) == Board::EdgeWall || board.get_edge(x, y, lineType.b) == Board::EdgeWall)
  600.                             continue;
  601.  
  602.                         if (board.get_neighbor_cell(x, y, lineType.a) == Board::CellFullLine || board.get_edge(x, y, lineType.b) == Board::CellFullLine)
  603.                             continue;
  604.                        
  605.                         Board copy = board;
  606.                         copy.set_edge(x, y, lineType.a, board.EdgeLine);
  607.                         copy.set_edge(x, y, lineType.b, board.EdgeLine);
  608.                         SolveResult result = deterministic_solve_board(copy);
  609.  
  610.                         if (result == ResultInvalid)
  611.                             continue;
  612.                         else if (result == ResultSolved)
  613.                         {
  614.                             board = copy;
  615.                             return ResultSolved;
  616.                         }
  617.                         else if (result == ResultIndeterminate)
  618.                         {
  619.                             possible++;
  620.                             lastSolve = i;
  621.                         }
  622.                     }
  623.                     if (possible == 0)
  624.                         return ResultInvalid;
  625.  
  626.                     if (possible == 1)
  627.                     {
  628.                         CellLine lineType = cellLines[lastSolve];
  629.                         board.set_edge(x, y, lineType.a, board.EdgeLine);
  630.                         board.set_edge(x, y, lineType.b, board.EdgeLine);
  631.                         SolveResult result = deterministic_solve_board(board);
  632.                         assert(result == ResultIndeterminate);
  633.                         didSomething = true;
  634.                     }
  635.                 }
  636.             }
  637.         }
  638.     }
  639.     while (didSomething);
  640.  
  641.     return ResultIndeterminate;
  642. }
  643.  
  644. void load_board(Board& board, const char *file)
  645. {
  646.     int w, h, comp;
  647.     stbi_uc *data = stbi_load(file, &w, &h, &comp, 0);
  648.  
  649.     auto get_data = [&](int x, int y, Direction dir)
  650.     {
  651.         Point p = get_offset(x * 2 + 1, y* 2 + 1, dir);
  652.         return data[(p.x + p.y * w) * comp] < 50;
  653.     };
  654.  
  655.     board.width = (w - 1) / 2;
  656.     board.height = (h - 1) / 2;
  657.  
  658.     board.cell_states.resize(board.width * board.height, Board::CellEmpty);
  659.     board.horizontal_edges.resize(board.width * (board.height + 1), Board::EdgeEmpty);
  660.     board.vertical_edges.resize((board.width + 1) * board.height, Board::EdgeEmpty);
  661.  
  662.     for (int y = 0; y < board.height; y++)
  663.     {
  664.         board.set_edge(0, y, Left, Board::EdgeDoor);
  665.         board.set_edge(board.width - 1, y, Right, Board::EdgeDoor);
  666.     }
  667.     for (int x = 0; x < board.width; x++)
  668.     {
  669.         board.set_edge(x, 0, Up, Board::EdgeDoor);
  670.         board.set_edge(x, board.height - 1, Down, Board::EdgeDoor);
  671.     }
  672.  
  673.     for (int y = 0; y < board.height; y++)
  674.     {
  675.         for (int x = 0; x < board.width; x++)
  676.         {
  677.             for (int i = 0; i < 4; i++)
  678.             {
  679.                 if (get_data(x, y, (Direction)i))
  680.                 {
  681.                     board.set_edge(x, y, (Direction)i, Board::EdgeWall);
  682.                 }
  683.             }
  684.         }
  685.     }
  686.  
  687.     stbi_image_free(data);
  688. }
  689.  
  690. int main(int argc, char** argv)
  691. {
  692.     Board board;
  693.  
  694.     load_board(board, "map11.bmp");
  695.  
  696.     draw_board(board);
  697.  
  698.     printf("Deterministic\n");
  699.  
  700.     auto before_det = std::chrono::high_resolution_clock::now();
  701.     deterministic_solve_board(board);
  702.     auto after_det = std::chrono::high_resolution_clock::now();
  703.  
  704.     draw_board(board);
  705.  
  706.     printf("Single option\n");
  707.  
  708.     auto before_opt = std::chrono::high_resolution_clock::now();
  709.     SolveResult res = single_option_solve_board(board);
  710.     auto after_opt = std::chrono::high_resolution_clock::now();
  711.  
  712.     draw_board(board);
  713.  
  714.     printf("Deterministic solve: %.1f ms\n", std::chrono::duration_cast<std::chrono::microseconds>(after_det - before_det).count() / 1000.0);
  715.     printf("Single option solve: %.1f ms\n", std::chrono::duration_cast<std::chrono::microseconds>(after_opt - before_opt).count() / 1000.0);
  716.  
  717.     getchar();
  718. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement