BaR5uk

Level.java

Dec 4th, 2021 (edited)
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 16.71 KB | None | 0 0
  1. //      Sokoban puzzles auto generator
  2. //      https://dxdy.ru/post1541620.html#p1541620
  3. //      2021 dec B@R5uk
  4.  
  5. import java.util.*;
  6.  
  7. public class Level {
  8.    
  9.     private static final int NO_CELL = -1;
  10.    
  11.     private static final int
  12.             MAZE_CELL_EMPTY        =  0,
  13.             MAZE_CELL_WALL         = -1,
  14.             MAZE_CELL_UNREACHABLE  = -2,
  15.             MAZE_CELL_EMPTY_MARKED = -3,
  16.             MAZE_CELL_EMPTY_ANGLE  = -4,
  17.             MAZE_CELL_WALL_MARKED  = -5;
  18.    
  19.     private int mazeWidth, mazeHeight, cellsNumber;
  20.     private int [] [] mazeMap, cellMap, neighborCells, overNeighborCells;
  21.     private CellVector [] cellVectors;
  22.     private char [] [] displayBilletDots, displayBilletSpcs, displayBuffer;
  23.    
  24.     public Level (String [] levelLines) {
  25.         int k, l, m, n, minX, maxX, minY, maxY, cell;
  26.         int [] [] tmpMap;
  27.         char [] charBufferDots, charBufferSpcs;
  28.         String line;
  29.         CellVector border, newVector, vector;
  30.         Queue <CellVector> queue = new ArrayDeque <> ();
  31.        
  32.         //      Determining map dimensions
  33.         mazeWidth = 0;
  34.         mazeHeight = levelLines .length;
  35.         for (k = 0; mazeHeight > k; ++k) {
  36.             if (mazeWidth < levelLines [k] .length ()) {
  37.                 mazeWidth = levelLines [k] .length ();
  38.             }
  39.         }
  40.        
  41.         //      Parsing source strings
  42.         mazeMap = new int [mazeHeight + 2] [mazeWidth + 2];
  43.         for (k = 0, m = 1; mazeHeight > k; ++k, ++m) {
  44.             line = levelLines [k];
  45.             Arrays .fill (mazeMap [m], MAZE_CELL_EMPTY);
  46.             for (l = 0, n = 1; line .length () > l; ++l, ++n) {
  47.                 if ('#' == line .charAt (l)) {
  48.                     mazeMap [m] [n] = MAZE_CELL_WALL;
  49.                 }
  50.             }
  51.         }
  52.        
  53.         //      Initializing border filling
  54.         mazeHeight += 2;
  55.         mazeWidth  += 2;
  56.         border = new CellVector (mazeWidth, mazeHeight);
  57.        
  58.         for (k = 0; mazeHeight > k; ++k) {
  59.             newVector = new CellVector (0, k);
  60.             newVector .setCell (mazeMap, MAZE_CELL_WALL);
  61.             queue .add (newVector);
  62.             newVector = new CellVector (mazeWidth - 1, k);
  63.             newVector .setCell (mazeMap, MAZE_CELL_WALL);
  64.             queue .add (newVector);
  65.         }
  66.        
  67.         for (k = 1; mazeWidth - 1 > k; ++k) {
  68.             newVector = new CellVector (k, 0);
  69.             newVector .setCell (mazeMap, MAZE_CELL_WALL);
  70.             queue .add (newVector);
  71.             newVector = new CellVector (k, mazeHeight - 1);
  72.             newVector .setCell (mazeMap, MAZE_CELL_WALL);
  73.             queue .add (newVector);
  74.         }
  75.        
  76.         //      Filling border cells with walls
  77.         while (!queue .isEmpty ()) {
  78.             vector = queue .remove ();
  79.             for (k = 0; 4 > k; ++k) {
  80.                 newVector = vector .neighbour (k);
  81.                 if (newVector .outOfBorder (border)) {
  82.                     continue;
  83.                 }
  84.                 if (MAZE_CELL_EMPTY != newVector .getCell (mazeMap)) {
  85.                     continue;
  86.                 }
  87.                 newVector .setCell (mazeMap, MAZE_CELL_WALL);
  88.                 queue .add (newVector);
  89.             }
  90.         }
  91.        
  92.         //      Looking for first inner cell (topmost, leftmost)
  93.         newVector = null;
  94.         outerloop:
  95.         for (k = 1; mazeHeight - 1 > k; ++k) {
  96.             for (l = 1; mazeWidth - 1 > l; ++l) {
  97.                 if (MAZE_CELL_EMPTY == mazeMap [k] [l]) {
  98.                     newVector = new CellVector (l, k);
  99.                     break outerloop;
  100.                 }
  101.             }
  102.         }
  103.         if (null == newVector) {
  104.             throw new IllegalArgumentException ("\n\nMaze is non-enclosed.\n");
  105.         }
  106.        
  107.         //      Determining connected inner area
  108.         queue .add (newVector);
  109.         minX = maxX = newVector .getX ();
  110.         minY = maxY = newVector .getY ();
  111.         --minY;
  112.         while (!queue .isEmpty ()) {
  113.             vector = queue .remove ();
  114.             for (k = 0; 4 > k; ++k) {
  115.                 newVector = vector .neighbour (k);
  116.                 switch (newVector .getCell (mazeMap)) {
  117.                 case MAZE_CELL_EMPTY:
  118.                 case MAZE_CELL_EMPTY_ANGLE:
  119.                     newVector .setCell (mazeMap, MAZE_CELL_EMPTY_MARKED);
  120.                     queue .add (newVector);
  121.                     break;
  122.                 case MAZE_CELL_WALL:
  123.                     newVector .setCell (mazeMap, MAZE_CELL_WALL_MARKED);
  124.                 case MAZE_CELL_WALL_MARKED:
  125.                 case MAZE_CELL_EMPTY_MARKED:
  126.                     break;
  127.                 }
  128.                 if (minX > newVector .getX ()) {
  129.                     minX = newVector .getX ();
  130.                 }
  131.                 if (maxX < newVector .getX ()) {
  132.                     maxX = newVector .getX ();
  133.                 }
  134.                 if (maxY < newVector .getY ()) {
  135.                     maxY = newVector .getY ();
  136.                 }
  137.             }
  138.             for (; 8 > k; ++k) {
  139.                 newVector = vector .neighbour (k);
  140.                 switch (newVector .getCell (mazeMap)) {
  141.                 case MAZE_CELL_EMPTY:
  142.                     newVector .setCell (mazeMap, MAZE_CELL_EMPTY_ANGLE);
  143.                     break;
  144.                 case MAZE_CELL_WALL:
  145.                     newVector .setCell (mazeMap, MAZE_CELL_WALL_MARKED);
  146.                 case MAZE_CELL_WALL_MARKED:
  147.                 case MAZE_CELL_EMPTY_MARKED:
  148.                 case MAZE_CELL_EMPTY_ANGLE:
  149.                     break;
  150.                 }
  151.             }
  152.         }
  153.        
  154.         //      Reworking maze map
  155.         mazeWidth  = maxX - minX + 1;
  156.         mazeHeight = maxY - minY + 1;
  157.         tmpMap = mazeMap;
  158.         mazeMap = new int [mazeHeight] [mazeWidth];
  159.         for (k = minY, m = 0; maxY >= k; ++k, ++m) {
  160.             for (l = minX, n = 0; maxX >= l; ++l, ++n) {
  161.                 newVector = new CellVector (n, m);
  162.                 switch (tmpMap [k] [l]) {
  163.                 case MAZE_CELL_WALL:
  164.                 case MAZE_CELL_EMPTY:
  165.                     newVector .setCell (mazeMap, MAZE_CELL_UNREACHABLE);
  166.                     break;
  167.                 case MAZE_CELL_WALL_MARKED:
  168.                 case MAZE_CELL_EMPTY_ANGLE:
  169.                     newVector .setCell (mazeMap, MAZE_CELL_WALL);
  170.                     break;
  171.                 case MAZE_CELL_EMPTY_MARKED:
  172.                     newVector .setCell (mazeMap, MAZE_CELL_EMPTY);
  173.                     queue .add (newVector);
  174.                     break;
  175.                 }
  176.             }
  177.         }
  178.        
  179.         //      Performing indexing of cells
  180.         cellsNumber = queue .size ();
  181.         cellVectors = queue .toArray (new CellVector [cellsNumber]);
  182.         cellMap = new int [mazeHeight] [mazeWidth];
  183.         for (k = 0; mazeHeight > k; ++k) {
  184.             Arrays .fill (cellMap [k], NO_CELL);
  185.         }
  186.         for (k = 0; cellsNumber > k; ++k) {
  187.             cellVectors [k] .setCell (cellMap, k);
  188.         }
  189.        
  190.         //      Building cell adjacency tables
  191.         neighborCells     = new int [cellsNumber] [4];
  192.         overNeighborCells = new int [cellsNumber] [4];
  193.         for (k = 0; cellsNumber > k; ++k) {
  194.             Arrays .fill (overNeighborCells [k], NO_CELL);
  195.             vector = cellVectors [k];
  196.             for (l = 0; 4 > l; ++l) {
  197.                 newVector = vector .neighbour (l);
  198.                 cell = newVector .getCell (cellMap);
  199.                 neighborCells [k] [l] = cell;
  200.                 if (NO_CELL == cell) {
  201.                     continue;
  202.                 }
  203.                 newVector = newVector .neighbour (l);
  204.                 overNeighborCells [k] [l] = newVector .getCell (cellMap);
  205.             }
  206.         }
  207.        
  208.         //      Initializing display data
  209.         displayBilletDots = new char [mazeHeight] [];
  210.         displayBilletSpcs = new char [mazeHeight] [];
  211.         displayBuffer     = new char [mazeHeight] [];
  212.         charBufferDots = new char [mazeWidth];
  213.         charBufferSpcs = new char [mazeWidth];
  214.         for (k = 0; mazeHeight > k; ++k) {
  215.             m = -1;
  216.             for (l = 0; mazeWidth > l; ++l) {
  217.                 switch (mazeMap [k] [l]) {
  218.                 case MAZE_CELL_EMPTY:
  219.                     charBufferDots [l] = '.';
  220.                     charBufferSpcs [l] = ' ';
  221.                     m = l;
  222.                     break;
  223.                 case MAZE_CELL_WALL:
  224.                     charBufferDots [l] = '#';
  225.                     charBufferSpcs [l] = '#';
  226.                     m = l;
  227.                     break;
  228.                 default:
  229.                     charBufferDots [l] = ' ';
  230.                     charBufferSpcs [l] = ' ';
  231.                     break;
  232.                 }
  233.             }
  234.             ++m;
  235.             displayBilletDots [k] = Arrays .copyOf (charBufferDots, m);
  236.             displayBilletSpcs [k] = Arrays .copyOf (charBufferSpcs, m);
  237.             displayBuffer     [k] = new char [m];
  238.         }
  239.     }
  240.    
  241.     //      ================================================
  242.    
  243.     public int getCellsNumber () {
  244.         return cellsNumber;
  245.     }
  246.    
  247.     //      ================================================
  248.    
  249.     public StateSpace buildStateSpace (int boxesNumber) {
  250.         int k, l, agentCell, nextCell, overCell;
  251.         StateSpace stateSpace = new StateSpace (cellsNumber, boxesNumber);
  252.         StateGenerator stateGenerator = new StateGenerator (boxesNumber);
  253.         long stateId;
  254.         int [] boxCells, newBoxCells;
  255.         boolean [] occupiedCells;
  256.        
  257.         do {
  258.             stateSpace .addState (stateGenerator .boxCells, stateGenerator .agentCell);
  259.         } while (stateGenerator .hasNext ());
  260.        
  261.         boxCells    = new int [boxesNumber];
  262.         newBoxCells = new int [boxesNumber];
  263.         occupiedCells = new boolean [cellsNumber];
  264.        
  265.         for (k = 0; stateSpace .getStatesNumber () > k; ++k) {
  266.             stateId = stateSpace .getStateIdByIndex (k);
  267.             agentCell = stateSpace .unpackStateFromId (stateId, boxCells, occupiedCells);
  268.             for (l = 0; 4 > l; ++l) {
  269.                 nextCell = neighborCells [agentCell] [l];
  270.                 if (NO_CELL == nextCell) {
  271.                     continue;
  272.                 }
  273.                 if (!occupiedCells [nextCell]) {
  274.                     stateSpace .linkStates (
  275.                             k,
  276.                             stateSpace .getStateIndexById (stateSpace .replaceAgentCell (stateId, nextCell)),
  277.                             l);
  278.                     continue;
  279.                 }
  280.                 overCell = overNeighborCells [agentCell] [l];
  281.                 if (NO_CELL == overCell) {
  282.                     continue;
  283.                 }
  284.                 if (occupiedCells [overCell]) {
  285.                     continue;
  286.                 }
  287.                 System .arraycopy (boxCells, 0, newBoxCells, 0, boxesNumber);
  288.                 newBoxCells [Arrays .binarySearch (boxCells, nextCell)] = overCell;
  289.                 stateSpace .linkStates (
  290.                         k,
  291.                         stateSpace .getStateIndex (newBoxCells, nextCell),
  292.                         l);
  293.                 }
  294.         }
  295.        
  296.         return stateSpace;
  297.     }
  298.    
  299.     //      ================================================
  300.    
  301.     private class StateGenerator {
  302.        
  303.         int boxesNumber, agentCell, boxCell, boxIndex, counter;
  304.         int [] boxCells;
  305.         char [] stringBuffer;
  306.        
  307.         public StateGenerator (int argBoxesNumber) {
  308.             boxesNumber = argBoxesNumber;
  309.             boxCells = new int [boxesNumber];
  310.             for (int k = 0; boxesNumber > k; ++k) {
  311.                 boxCells [k] = k;
  312.             }
  313.             agentCell = cellsNumber - 1;
  314.             boxCell   = boxesNumber;
  315.             boxIndex  = boxesNumber - 1;
  316.             counter   = 0;
  317.         }
  318.        
  319.         public boolean hasNext () {
  320.             do {
  321.                 if (0 == agentCell) {
  322.                     if (0 > boxIndex) {
  323.                         return false;
  324.                     }
  325.                     nextBoxesPlacement ();
  326.                     agentCell = cellsNumber;
  327.                 }
  328.                 --agentCell;
  329.             } while (0 <= Arrays .binarySearch (boxCells, agentCell));
  330.             return true;
  331.         }
  332.        
  333.         private void nextBoxesPlacement () {
  334.             int startIndex, limitValue;
  335.            
  336.             startIndex = boxIndex;
  337.             limitValue = boxCell;
  338.             do {
  339.                 boxCells [boxIndex] = boxCell;
  340.                 ++boxCell;
  341.                 ++boxIndex;
  342.             } while (boxesNumber > boxIndex);
  343.             if (cellsNumber > boxCell) {
  344.                 --boxIndex;
  345.                 return;
  346.             }
  347.             boxIndex = startIndex;
  348.             while (true) {
  349.                 --boxIndex;
  350.                 if (0 > boxIndex) {
  351.                     return;
  352.                 }
  353.                 --limitValue;
  354.                 boxCell = boxCells [boxIndex];
  355.                 if (boxCell < limitValue) {
  356.                     ++boxCell;
  357.                     return;
  358.                 }
  359.             }
  360.         }
  361.        
  362.         @SuppressWarnings ("unused")
  363.         public void display () {
  364.             System .out .println (String .format ("%6d   |%s|", counter++, toString ()));
  365.         }
  366.        
  367.         @Override
  368.         public String toString () {
  369.             if (null == stringBuffer) {
  370.                 stringBuffer = new char [cellsNumber];
  371.             }
  372.             Arrays .fill (stringBuffer, '.');
  373.             for (int k = 0; boxesNumber > k; ++k) {
  374.                 stringBuffer [boxCells [k]] = 'o';
  375.             }
  376.             stringBuffer [agentCell] = 'x';
  377.             return new String (stringBuffer);
  378.         }
  379.     }
  380.    
  381.     //      ================================================
  382.    
  383.     private void fillDisplayBuffer (char [] [] displayBillet) {
  384.         for (int k = 0; mazeHeight > k; ++k) {
  385.             System .arraycopy (displayBillet [k], 0, displayBuffer [k], 0, displayBillet [k] .length);
  386.         }
  387.     }
  388.    
  389.     //      ================================================
  390.    
  391.     public void displayMaze () {
  392.         for (int k = 0; mazeHeight > k; ++k) {
  393.             System .out .println (new String (displayBilletDots [k]));
  394.         }
  395.     }
  396.    
  397.     //      ================================================
  398.    
  399.     public void displayState (int index, StateSpace stateSpace) {
  400.         int k, agentCell, boxesNumber = stateSpace .getBoxesNumber ();
  401.         int [] boxCells = new int [boxesNumber];
  402.         fillDisplayBuffer (displayBilletDots);
  403.         agentCell = stateSpace .unpackStateByIndex (index, boxCells);
  404.         for (k = 0; boxesNumber > k; ++k) {
  405.             cellVectors [boxCells [k]] .setCell (displayBuffer, '$');
  406.         }
  407.         cellVectors [agentCell] .setCell (displayBuffer, '@');
  408.         for (k = 0; mazeHeight > k; ++k) {
  409.             System .out .println (new String (displayBuffer [k]));
  410.         }
  411.     }
  412.    
  413.     public void displayPlacement (int index, StateSpace stateSpace) {
  414.         int k, boxesNumber = stateSpace .getBoxesNumber ();
  415.         int [] boxCells = new int [boxesNumber];
  416.         fillDisplayBuffer (displayBilletDots);
  417.         stateSpace .unpackStateByIndex (index, boxCells);
  418.         for (k = 0; boxesNumber > k; ++k) {
  419.             cellVectors [boxCells [k]] .setCell (displayBuffer, '$');
  420.         }
  421.         for (k = 0; mazeHeight > k; ++k) {
  422.             System .out .println (new String (displayBuffer [k]));
  423.         }
  424.     }
  425.    
  426.     public void displayPuzzle (int start, int end, StateSpace stateSpace) {
  427.         int k, cell, boxesNumber = stateSpace .getBoxesNumber ();
  428.         int [] boxCells = new int [boxesNumber];
  429.         fillDisplayBuffer (displayBilletSpcs);
  430.         stateSpace .unpackStateByIndex (end, boxCells);
  431.         for (k = 0; boxesNumber > k; ++k) {
  432.             cellVectors [boxCells [k]] .setCell (displayBuffer, '.');
  433.         }
  434.         cell = stateSpace .unpackStateByIndex (start, boxCells);
  435.         if (' ' == cellVectors [cell] .getCell (displayBuffer)) {
  436.             cellVectors [cell] .setCell (displayBuffer, '@');
  437.         } else {
  438.             cellVectors [cell] .setCell (displayBuffer, '+');
  439.         }
  440.         for (k = 0; boxesNumber > k; ++k) {
  441.             cell = boxCells [k];
  442.             if (' ' == cellVectors [cell] .getCell (displayBuffer)) {
  443.                 cellVectors [cell] .setCell (displayBuffer, '$');
  444.             } else {
  445.                 cellVectors [cell] .setCell (displayBuffer, '*');
  446.             }
  447.         }
  448.         for (k = 0; mazeHeight > k; ++k) {
  449.             System .out .println (new String (displayBuffer [k]));
  450.         }
  451.     }
  452. }
  453.  
Add Comment
Please, Sign In to add comment