BaR5uk

StateSpace.java

Dec 4th, 2021 (edited)
320
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 23.17 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 StateSpace {
  8.    
  9.     public static final int NO_STATE = -1;
  10.     public static final int CELLS_LIMIT = 57;
  11.     public static final int STATES_LIMIT = 10_000_000;
  12.     public static final short COUNT_INFINITY = Short .MAX_VALUE;
  13.    
  14.     private static long iterCount = -1L;
  15.    
  16.     private int cellsNumber, boxesNumber, freeNumber, statesNumber, stateIndex;
  17.     private long [] stateIds;
  18.     private int [] childStates, parentStates;
  19.     private short [] moveCounts, pushCounts;
  20.     private boolean spaceFilledFlag;
  21.     private SortedSet <Integer> queue;
  22.    
  23.     public StateSpace (int argCellsNumber, int argBoxesNumber) {
  24.         cellsNumber = argCellsNumber;
  25.         boxesNumber = argBoxesNumber;
  26.         freeNumber  = cellsNumber - boxesNumber;
  27.         if (CELLS_LIMIT < cellsNumber) {
  28.             throw new IllegalArgumentException ("\n\nNumber of cells out of range.\n");
  29.         }
  30.         if (0 > boxesNumber || cellsNumber <= boxesNumber) {
  31.             throw new IllegalArgumentException ("\n\nNumber of boxes out of range.\n");
  32.         }
  33.         long tmpStatesNumber = getStatesNumber (cellsNumber, boxesNumber);
  34.         if (-1L == tmpStatesNumber || STATES_LIMIT < tmpStatesNumber) {
  35.             throw new IllegalArgumentException ("\n\nNumber of states out of range.\n");
  36.         }
  37.         statesNumber = (int) tmpStatesNumber;
  38.         stateIds     = new long  [statesNumber];
  39.         stateIndex = statesNumber;
  40.         spaceFilledFlag = false;
  41.         childStates  = new int   [statesNumber * 4];
  42.         parentStates = new int   [statesNumber * 4];
  43.         Arrays .fill (childStates,  NO_STATE);
  44.         Arrays .fill (parentStates, NO_STATE);
  45.         moveCounts   = new short [statesNumber];
  46.         pushCounts   = new short [statesNumber];
  47.         queue = new TreeSet <> (new MovePriorityComparator ());
  48.     }
  49.    
  50.     //      ================================================
  51.    
  52.     public int getCellsNumber () {
  53.         return cellsNumber;
  54.     }
  55.    
  56.     public int getBoxesNumber () {
  57.         return boxesNumber;
  58.     }
  59.    
  60.     public int getStatesNumber () {
  61.         return statesNumber;
  62.     }
  63.    
  64.     public void displayStats () {
  65.         System .out .println (String .format ("\nNumber of cells: %14d", cellsNumber));
  66.         System .out .println (String .format ("Number of boxes: %14d", boxesNumber));
  67.         System .out .println (String .format ("Number of states: %13s", Helper .spacedNumber (statesNumber)));
  68.     }
  69.    
  70.     //      ================================================
  71.    
  72.     public int getMoveCount (int stateIndex) {
  73.         return moveCounts [stateIndex];
  74.     }
  75.    
  76.     public int getPushCount (int stateIndex) {
  77.         return pushCounts [stateIndex];
  78.     }
  79.    
  80.     //      ================================================
  81.    
  82.     public void addState (int [] boxCells, int cell) {
  83.         if (spaceFilledFlag) {
  84.             throw new RuntimeException ("\n\nState Space has already been filled.\n");
  85.         }
  86.         long id = packState (boxCells, cell);
  87.         //System .out .println (Long .toHexString (id) + " " + Arrays .toString (boxCells));
  88.         if (statesNumber != stateIndex && 0 >= Long .compare (stateIds [stateIndex], id)) {
  89.             throw new IllegalArgumentException ("\n\nProper order of state IDs is broken.\n");
  90.         }
  91.         --stateIndex;
  92.         stateIds [stateIndex] = id;
  93.         if (0 == stateIndex) {
  94.             spaceFilledFlag = true;
  95.         }
  96.     }
  97.    
  98.     //      ================================================
  99.    
  100.     public void linkStates (int parentState, int childState, int move) {
  101.         if (0 > move || 4 <= move) {
  102.             throw new IllegalArgumentException ("\n\nMove index out of range.\n");
  103.         }
  104.         if (0 > parentState || statesNumber <= parentState) {
  105.             throw new IllegalArgumentException ("\n\nParent state index out of range.\n");
  106.         }
  107.         if (0 > childState || statesNumber <= childState) {
  108.             throw new IllegalArgumentException ("\n\nChild state index out of range.\n");
  109.         }
  110.         childStates      [4 * parentState + move]  = childState;
  111.         if (isSamePlacement (parentState, childState)) {
  112.             parentStates [4 * childState  + move]  = parentState;
  113.         } else {
  114.             parentStates [4 * childState  +
  115.                    CellVector .reverseMove (move)] = parentState;
  116.         }
  117.     }
  118.    
  119.     //      ================================================
  120.    
  121.     public int getStateIndexById (long stateId) {
  122.         if (!spaceFilledFlag) {
  123.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  124.         }
  125.         int stateIndex = Arrays .binarySearch (stateIds, stateId);
  126.         if (0 > stateIndex) {
  127.             throw new IllegalArgumentException ("\n\nIllegal state ID.\n");
  128.         }
  129.         return stateIndex;
  130.     }
  131.    
  132.     //      ================================================
  133.    
  134.     public int getStateIndex (int [] boxCells, int cell) {
  135.         if (!spaceFilledFlag) {
  136.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  137.         }
  138.         return Arrays .binarySearch (stateIds, packState (boxCells, cell));
  139.     }
  140.    
  141.     //      ================================================
  142.    
  143.     public long getStateIdByIndex (int stateIndex) {
  144.         if (!spaceFilledFlag) {
  145.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  146.         }
  147.         return stateIds [stateIndex];
  148.     }
  149.    
  150.     //      ================================================
  151.    
  152.     public int unpackStateByIndex (int stateIndex, int [] boxCells, boolean [] occupiedCells) {
  153.         if (!spaceFilledFlag) {
  154.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  155.         }
  156.         return unpackStateFromId (stateIds [stateIndex], boxCells, occupiedCells);
  157.     }
  158.    
  159.     //      ================================================
  160.    
  161.     public int unpackStateByIndex (int stateIndex, int [] boxCells) {
  162.         if (!spaceFilledFlag) {
  163.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  164.         }
  165.         return unpackStateFromId (stateIds [stateIndex], boxCells);
  166.     }
  167.    
  168.     //      ================================================
  169.    
  170.     public long packState (int [] boxCells, int agentCell) {
  171.         int cell;
  172.         long stateId;
  173.         if (boxesNumber != boxCells .length) {
  174.             throw new IllegalArgumentException ("\n\nIncorrect number of boxes.\n");
  175.         }
  176.         if (0 > agentCell || cellsNumber <= agentCell) {
  177.             throw new IllegalArgumentException ("\n\nCell index out of range.\n");
  178.         }
  179.         stateId = agentCell;
  180.         for (int k = 0; boxesNumber > k; ++k) {
  181.             cell = boxCells [k];
  182.             if (cellsNumber <= cell) {
  183.                 throw new IllegalArgumentException ("\n\nCell index out of range.\n");
  184.             }
  185.             long value = 0x4000000000000000L >>> cell;
  186.             if (0L != (stateId & value)) {
  187.                 throw new IllegalArgumentException ("\n\nDuplicate cell index.\n");
  188.             }
  189.             stateId |= value;
  190.         }
  191.         return stateId;
  192.     }
  193.    
  194.     //      ================================================
  195.    
  196.     public int unpackStateFromId (long stateId, int [] boxCells, boolean [] occupiedCells) {
  197.         int k = 0, index = 0;
  198.         long mask = 0x4000000000000000L;
  199.         Arrays .fill (occupiedCells, false);
  200.         for (; cellsNumber > k; ++k, mask >>>= 1) {
  201.             if (0L == (stateId & mask)) {
  202.                 continue;
  203.             }
  204.             boxCells [index] = k;
  205.             occupiedCells [k] = true;
  206.             ++index;
  207.         }
  208.         if (boxesNumber != index) {
  209.             throw new RuntimeException ("\n\nNumber of boxes does not concur.\n");
  210.         }
  211.         return (int) (stateId & 0x3F);
  212.     }
  213.    
  214.     //      ================================================
  215.    
  216.     public int unpackStateFromId (long stateId, int [] boxCells) {
  217.         int k = 0, index = 0;
  218.         long mask = 0x4000000000000000L;
  219.         for (; cellsNumber > k; ++k, mask >>>= 1) {
  220.             if (0L == (stateId & mask)) {
  221.                 continue;
  222.             }
  223.             boxCells [index] = k;
  224.             ++index;
  225.         }
  226.         if (boxesNumber != index) {
  227.             throw new RuntimeException ("\n\nNumber of boxes does not concur.\n");
  228.         }
  229.         return (int) (stateId & 0x3F);
  230.     }
  231.    
  232.     //      ================================================
  233.    
  234.     public long replaceAgentCell (long stateId, int agentCell) {
  235.         if (0 > agentCell || cellsNumber <= agentCell) {
  236.             throw new IllegalArgumentException ("\n\nCell index out of range.\n");
  237.         }
  238.         return stateId & 0xFFFFFFFFFFFFFFC0L | agentCell;
  239.     }
  240.    
  241.     //      ================================================
  242.    
  243.     public boolean isSamePlacement (int stateIndex1, int stateIndex2) {
  244.         if (!spaceFilledFlag) {
  245.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  246.         }
  247.         return 0 == ((stateIds [stateIndex1] ^ stateIds [stateIndex2]) & 0xFFFFFFFFFFFFFFC0L);
  248.     }
  249.    
  250.     //      ================================================
  251.    
  252.     public int findMostDistantState () {
  253.         int k, bestIndex = NO_STATE;
  254.         short moveCount, pushCount, bestMoveCount = -1, bestPushCount = -1;
  255.         for (k = 0; statesNumber > k; ++k) {
  256.             moveCount = moveCounts [k];
  257.             if (COUNT_INFINITY == moveCount) {
  258.                 continue;
  259.             }
  260.             pushCount = pushCounts [k];
  261.             if (DoubleComp .isGreater (moveCount, bestMoveCount, pushCount, bestPushCount)) {
  262.                 bestMoveCount = moveCount;
  263.                 bestPushCount = pushCount;
  264.                 bestIndex = k;
  265.             }
  266.         }
  267.         return bestIndex;
  268.     }
  269.    
  270.     //      ================================================
  271.    
  272.     public void performForwardDijkstraSearch (int startState) {
  273.         int state, newState, childIndex, childIndexLimit;
  274.         short moveCount, pushCount, pushCountM1;
  275.        
  276.         if (!spaceFilledFlag) {
  277.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  278.         }
  279.         Arrays .fill (moveCounts, COUNT_INFINITY);
  280.         Arrays .fill (pushCounts, COUNT_INFINITY);
  281.         queue .clear ();
  282.         queue .add (startState);
  283.         moveCounts [startState] = 0;
  284.         pushCounts [startState] = 0;
  285.        
  286.         while (!queue .isEmpty ()) {
  287.             state = queue .first ();
  288.             queue .remove (state);
  289.             moveCount   = (short) (moveCounts [state] + 1);
  290.             pushCount   = (short) (pushCounts [state] + 1);
  291.             pushCountM1 = pushCounts [state];
  292.             childIndex      = 4 * state;
  293.             childIndexLimit = 4 * state + 4;
  294.             for (; childIndexLimit > childIndex; ++childIndex) {
  295.                 newState = childStates [childIndex];
  296.                 if (NO_STATE == newState) {
  297.                     continue;
  298.                 }
  299.                 if (isSamePlacement (state, newState)) {
  300.                     if (DoubleComp .isGreater (
  301.                             moveCounts [newState], moveCount,
  302.                             pushCounts [newState], pushCountM1)) {
  303.                         queue .remove (newState);
  304.                         moveCounts [newState] = moveCount;
  305.                         pushCounts [newState] = pushCountM1;
  306.                         queue .add (newState);
  307.                     }
  308.                 } else {
  309.                     if (DoubleComp .isGreater (
  310.                             moveCounts [newState], moveCount,
  311.                             pushCounts [newState], pushCount)) {
  312.                         queue .remove (newState);
  313.                         moveCounts [newState] = moveCount;
  314.                         pushCounts [newState] = pushCount;
  315.                         queue .add (newState);
  316.                     }
  317.                 }
  318.             }
  319.         }
  320.     }
  321.    
  322.     //      ================================================
  323.    
  324.     public void performBackwardDijkstraSearch (int startState) {
  325.         int state, newState, parentIndex, parentIndexLimit;
  326.         short moveCount, pushCount, pushCountM1;
  327.        
  328.         if (!spaceFilledFlag) {
  329.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  330.         }
  331.         Arrays .fill (moveCounts, COUNT_INFINITY);
  332.         Arrays .fill (pushCounts, COUNT_INFINITY);
  333.         queue .clear ();
  334.         queue .add (startState);
  335.         moveCounts [startState] = 0;
  336.         pushCounts [startState] = 0;
  337.        
  338.         while (!queue .isEmpty ()) {
  339.             state = queue .first ();
  340.             queue .remove (state);
  341.             moveCount   = (short) (moveCounts [state] + 1);
  342.             pushCount   = (short) (pushCounts [state] + 1);
  343.             pushCountM1 = pushCounts [state];
  344.             parentIndex      = 8 * state;
  345.             parentIndexLimit = 8 * state + 8;
  346.             for (; parentIndexLimit > parentIndex; ++parentIndex) {
  347.                 newState = parentStates [parentIndex];
  348.                 if (NO_STATE == newState) {
  349.                     continue;
  350.                 }
  351.                 if (isSamePlacement (state, newState)) {
  352.                     if (DoubleComp .isGreater (
  353.                             moveCounts [newState], moveCount,
  354.                             pushCounts [newState], pushCountM1)) {
  355.                         queue .remove (newState);
  356.                         moveCounts [newState] = moveCount;
  357.                         pushCounts [newState] = pushCountM1;
  358.                         queue .add (newState);
  359.                     }
  360.                 } else {
  361.                     if (DoubleComp .isGreater (
  362.                             moveCounts [newState], moveCount,
  363.                             pushCounts [newState], pushCount)) {
  364.                         queue .remove (newState);
  365.                         moveCounts [newState] = moveCount;
  366.                         pushCounts [newState] = pushCount;
  367.                         queue .add (newState);
  368.                     }
  369.                 }
  370.             }
  371.         }
  372.     }
  373.    
  374.     //      ================================================
  375.    
  376.     public void performMultistartDijkstraSearch (int startState) {
  377.         int state, newState, parentIndex, parentIndexLimit;
  378.         short moveCount, pushCount, pushCountM1;
  379.        
  380.         if (!spaceFilledFlag) {
  381.             throw new RuntimeException ("\n\nState Space has not yet been filled.\n");
  382.         }
  383.         Arrays .fill (moveCounts, COUNT_INFINITY);
  384.         Arrays .fill (pushCounts, COUNT_INFINITY);
  385.         queue .clear ();
  386.         startState = startState - startState % freeNumber;
  387.         for (newState = startState; startState + freeNumber > newState; ++newState) {
  388.             queue .add (newState);
  389.             moveCounts [newState] = 0;
  390.             pushCounts [newState] = 0;
  391.         }
  392.        
  393.         while (!queue .isEmpty ()) {
  394.             state = queue .first ();
  395.             queue .remove (state);
  396.             moveCount   = (short) (moveCounts [state] + 1);
  397.             pushCount   = (short) (pushCounts [state] + 1);
  398.             pushCountM1 = pushCounts [state];
  399.             parentIndex      = 4 * state;
  400.             parentIndexLimit = 4 * state + 4;
  401.             for (; parentIndexLimit > parentIndex; ++parentIndex) {
  402.                 newState = parentStates [parentIndex];
  403.                 if (NO_STATE == newState) {
  404.                     continue;
  405.                 }
  406.                 if (isSamePlacement (state, newState)) {
  407.                     if (DoubleComp .isGreater (
  408.                             moveCounts [newState], moveCount,
  409.                             pushCounts [newState], pushCountM1)) {
  410.                         queue .remove (newState);
  411.                         moveCounts [newState] = moveCount;
  412.                         pushCounts [newState] = pushCountM1;
  413.                         queue .add (newState);
  414.                     }
  415.                 } else {
  416.                     if (DoubleComp .isGreater (
  417.                             moveCounts [newState], moveCount,
  418.                             pushCounts [newState], pushCount)) {
  419.                         queue .remove (newState);
  420.                         moveCounts [newState] = moveCount;
  421.                         pushCounts [newState] = pushCount;
  422.                         queue .add (newState);
  423.                     }
  424.                 }
  425.             }
  426.         }
  427.     }
  428.    
  429.     //      ================================================
  430.    
  431.     public AnalysisResult performDistanceAnalysis () {
  432.         int startState, limitState, newState, state, parentIndex, parentIndexLimit;
  433.         short moveCount, pushCount, pushCountM1;
  434.         AnalysisResult result = new AnalysisResult ();
  435.         Arrays .fill (result .bestMoveCounts, (short) -1);
  436.         Arrays .fill (result .bestPushCounts, (short) -1);
  437.         Arrays .fill (result .bestPlacements, -1);
  438.         limitState = 0;
  439.         iterCount  = 0;
  440.         while (statesNumber > limitState) {
  441.             startState = limitState;
  442.             limitState += freeNumber;
  443.             Arrays .fill (moveCounts, COUNT_INFINITY);
  444.             Arrays .fill (pushCounts, COUNT_INFINITY);
  445.             queue .clear ();
  446.             for (newState = startState; limitState > newState; ++newState) {
  447.                 queue .add (newState);
  448.                 moveCounts [newState] = 0;
  449.                 pushCounts [newState] = 0;
  450.             }
  451.             while (!queue .isEmpty ()) {
  452.                 state = queue .first ();
  453.                 queue .remove (state);
  454.                 moveCount   = moveCounts [state];
  455.                 pushCount   = pushCounts [state];
  456.                 pushCountM1 = pushCount;
  457.                 if (DoubleComp .isGreater (
  458.                         moveCount, result .bestMoveCounts [state],
  459.                         pushCount, result .bestPushCounts [state])) {
  460.                     result .bestMoveCounts [state] = moveCount;
  461.                     result .bestPushCounts [state] = pushCount;
  462.                     result .bestPlacements [state] = startState;
  463.                 }
  464.                 ++iterCount;
  465.                 ++moveCount;
  466.                 ++pushCount;
  467.                 parentIndex      = 4 * state;
  468.                 parentIndexLimit = 4 * state + 4;
  469.                 for (; parentIndexLimit > parentIndex; ++parentIndex) {
  470.                     newState = parentStates [parentIndex];
  471.                     if (NO_STATE == newState) {
  472.                         continue;
  473.                     }
  474.                     if (isSamePlacement (state, newState)) {
  475.                         if (DoubleComp .isGreater (
  476.                                 moveCounts [newState], moveCount,
  477.                                 pushCounts [newState], pushCountM1)) {
  478.                             queue .remove (newState);
  479.                             moveCounts [newState] = moveCount;
  480.                             pushCounts [newState] = pushCountM1;
  481.                             queue .add (newState);
  482.                         }
  483.                     } else {
  484.                         if (DoubleComp .isGreater (
  485.                                 moveCounts [newState], moveCount,
  486.                                 pushCounts [newState], pushCount)) {
  487.                             queue .remove (newState);
  488.                             moveCounts [newState] = moveCount;
  489.                             pushCounts [newState] = pushCount;
  490.                             queue .add (newState);
  491.                         }
  492.                     }
  493.                 }
  494.             }
  495.         }
  496.         return result;
  497.     }
  498.    
  499.     public static long getIterCount () {
  500.         return iterCount;
  501.     }
  502.    
  503.     //      ================================================
  504.    
  505.     public class AnalysisResult {
  506.        
  507.         private short [] bestMoveCounts, bestPushCounts;
  508.         private int [] bestPlacements;
  509.        
  510.         private AnalysisResult () {
  511.             bestMoveCounts = new short [statesNumber];
  512.             bestPushCounts = new short [statesNumber];
  513.             bestPlacements = new int   [statesNumber];
  514.         }
  515.        
  516.         public int [] getBest () {
  517.             int state, bestState = -1, bestPlacement = -1, bestMoveCount = -1, bestPushCount = -1;
  518.             int [] result = new int [4];
  519.             for (state = 0; statesNumber > state; ++state) {
  520.                 if (DoubleComp .isGreater (
  521.                         bestMoveCounts [state], bestMoveCount,
  522.                         bestPushCounts [state], bestPushCount)) {
  523.                     bestState = state;
  524.                     bestPlacement = bestPlacements [state];
  525.                     bestMoveCount = bestMoveCounts [state];
  526.                     bestPushCount = bestPushCounts [state];
  527.                 }
  528.             }
  529.             result [0] = bestState;
  530.             result [1] = bestPlacement;
  531.             result [2] = bestMoveCount;
  532.             result [3] = bestPushCount;
  533.             return result;
  534.         }
  535.     }
  536.    
  537.     //      ================================================
  538.    
  539.     private class MovePriorityComparator implements Comparator <Integer> {
  540.         @Override
  541.         public int compare (Integer arg1, Integer arg2) {
  542.             int result;
  543.             if (0 != (result = moveCounts [arg1] - moveCounts [arg2])) {
  544.                 return result;
  545.             }
  546.             if (0 != (result = pushCounts [arg1] - pushCounts [arg2])) {
  547.                 return result;
  548.             }
  549.             return arg1 - arg2;
  550.         }
  551.     }
  552.    
  553.     //      ================================================
  554.    
  555.     public static String stateIdToHex (long stateId) {
  556.         return String .format ("%16H", stateId);
  557.     }
  558.    
  559.     //      ================================================
  560.    
  561.     public static long getStatesNumber (int cellsNumber, int boxesNumber) {
  562.         long result, value;
  563.         if (cellsNumber < boxesNumber) {
  564.             return -1L;
  565.         }
  566.         result = nchoosek (cellsNumber, boxesNumber);
  567.         if (-1L == result) {
  568.             return -1L;
  569.         }
  570.         value = cellsNumber - boxesNumber;
  571.         if (result > Long .MAX_VALUE / value) {
  572.             return -1L;
  573.         }
  574.         return result * value;
  575.     }
  576.    
  577.     //      ================================================
  578.    
  579.     public static long nchoosek (int n, int k) {
  580.         long result = 1, value;
  581.         if (0 > k || n < k) {
  582.             return 0;
  583.         }
  584.         k = Integer .min (k, n - k);
  585.         for (int l = 1; 0 < k; ++l, --k, --n) {
  586.             value = result / l;
  587.             //      a <= |_ b / c _|  ==>  a c <= b
  588.             //      a >  |_ b / c _|  ==>  a c >  b
  589.             if (value > Long .MAX_VALUE / n) {
  590.                 return -1;
  591.             }
  592.             result = value * n + result % l * n / l;
  593.             if (0 > result) {
  594.                 return -1;
  595.             }
  596.         }
  597.         return result;
  598.     }
  599. }
  600.  
Add Comment
Please, Sign In to add comment