Advertisement
Guest User

COTO TetPack Java Code - Rev. 1

a guest
Oct 16th, 2014
1,525
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 12.46 KB | None | 0 0
  1. import java.util.*;
  2.  
  3. public final class TetPack
  4. {
  5.     static final boolean DEBUG = false;
  6.  
  7.     /* The following constant should be consistent with the pieces defined by the Piece enum. */
  8.  
  9.     static final int OMINO = 4;
  10.  
  11.     static final char CLEAR_CELL = ' ';
  12.     static final char RESERVED_EMPTY_CELL = 'X';
  13.     static final char PLACEHOLDER_CELL = '*';
  14.  
  15.     public static void main( String[] sArgs ) {
  16.         final int iWidth,
  17.             nPcs = sArgs.length - 1;
  18.  
  19.         final EnumMap<Piece,Integer> M_Pcs = new EnumMap<>( Piece.class );
  20.         try {
  21.             iWidth = Integer.parseInt( sArgs[0] );
  22.             for( String s : Arrays.asList( sArgs ).subList( 1, sArgs.length ) ) {
  23.                 Piece Pc = Piece.fromChar( s.charAt( 0 ) );
  24.                 M_Pcs.put( Pc, M_Pcs.getOrDefault( Pc, 0 ) + 1 );
  25.             }
  26.         } catch( RuntimeException REx ) {
  27.             System.err.println( "Invalid argument(s).\nUsage: java ./TetPack.class <width> <shape> <shape> ...\n" );
  28.             throw REx;
  29.         }
  30.  
  31.         if( nPcs == 0 ) {
  32.             System.out.println( "Nothing to do!" );
  33.             System.exit( 1 );
  34.         }
  35.  
  36.         int iHeight0 = (int)Math.ceil( nPcs*OMINO / (double)iWidth ),
  37.             iHeight = iHeight0;
  38.  
  39.         Frame F;
  40.         while( !solveInFrame( F = new Frame( iWidth, iHeight, nPcs, M_Pcs ) ) )
  41.             if( iHeight >= iHeight0 + 3 ) {
  42.                 System.out.println( "Specified input cannot be packed." );
  43.                 System.exit( 2 );
  44.             } else iHeight++;
  45.  
  46.         F.dump();
  47.         if( F.isOptimal() )
  48.             System.out.println( "Displayed solution is optimal." );
  49.     }
  50.  
  51.     private static boolean solveInFrame( Frame F ) {
  52.         while( true ) {
  53.             if( solveWithReservedCells( F ) )
  54.                 return true;
  55.             if( !F.advanceReservedCellPermutation() )
  56.                 return false;
  57.         }
  58.     }
  59.  
  60.     private static boolean solveWithReservedCells( Frame F ) {
  61.         Deque<StackFrame> SFs = new LinkedList<>();
  62.  
  63.         SFs.push( new StackFrame( F.M_Pcs, F.firstEmptyCell( 0 ) ) );
  64.         while( !SFs.isEmpty() ) {
  65.             StackFrame SF = SFs.peek();
  66.  
  67.             if( SF.isPieceLoaded() )
  68.                 F.subtractPieceInFrame( SF );
  69.  
  70.             if( !SF.advance() ) {
  71.                 if( DEBUG ) {
  72.                     System.out.println( "\nConfiguration has no solutions:\n" );
  73.                     F.dump();
  74.                     try {
  75.                         Thread.sleep( 250 );
  76.                     } catch( InterruptedException IEx ) { /* ignore */ }
  77.                 }
  78.                 SFs.pop();
  79.                 continue;
  80.             }
  81.             if( F.canAddPieceInFrame( SF ) ) {
  82.                 F.addPieceInFrame( SF );
  83.  
  84.                 if( DEBUG ) {
  85.                     System.out.println( "\nAdded piece at stack level " + SFs.size() + ":\n" );
  86.                     F.dump();
  87.                     try {
  88.                         Thread.sleep( 250 );
  89.                     } catch( InterruptedException IEx ) { /* ignore */ }
  90.                 }
  91.                 if( SFs.size() == F.nPcs )
  92.                     return true;
  93.  
  94.                 SFs.push( SF.withoutCurrentPiece( F.firstEmptyCell( SF.iCell + 1 ) ) );
  95.             }
  96.         }
  97.         return false;
  98.     }
  99.  
  100.  
  101.     private static final class Frame {
  102.         final int iWidth;
  103.         final int iHeight;
  104.         final int nPcs;
  105.         final int nCell;
  106.         final int nEmpty;
  107.         final EnumMap<Piece,Integer> M_Pcs;
  108.         final char[] chCells;
  109.         final List<List<Integer>> nsEmptyByLine;
  110.  
  111.         final List<Integer> RESERVE_ALL;
  112.  
  113.         Frame( int iWidth, int iHeight, int nPcs, EnumMap<Piece,Integer> M_Pcs ) {
  114.             this.iWidth = iWidth;
  115.             this.iHeight = iHeight;
  116.             this.nPcs = nPcs;
  117.             this.M_Pcs = M_Pcs;
  118.  
  119.             nCell = iWidth*iHeight;
  120.             nEmpty = nCell - nPcs*OMINO;
  121.  
  122.             chCells = new char[nCell];
  123.             Arrays.fill( chCells, CLEAR_CELL );
  124.  
  125.             RESERVE_ALL = new ArrayList<>( iWidth );
  126.             for( int i = 0; i < iWidth; i++ )
  127.                 RESERVE_ALL.add( i );
  128.  
  129.             nsEmptyByLine = new ArrayList<>( iHeight );
  130.             initReservedCellPermutations();
  131.         }
  132.  
  133.         void initReservedCellPermutations() {
  134.             if( nEmpty == 0 )
  135.                 return;
  136.  
  137.             for( int i = 0; i < Math.min( nEmpty, iHeight ); i++ )
  138.                 nsEmptyByLine.add( new ArrayList<>( iWidth ) );
  139.  
  140.             arcFirstEnPerm( Math.min( nEmpty, iWidth ), 0 );
  141.             arcRepack( 1 );
  142.         }
  143.  
  144.         boolean advanceReservedCellPermutation() {
  145.             if( nEmpty == 0 )
  146.                 return false;
  147.  
  148.             int iLine = arcLowestOccupiedLine();
  149.             while( true ) {
  150.                 int nLineCells = nsEmptyByLine.get( iLine ).size();
  151.  
  152.                 if( arcNextPermForLine( iLine ) )
  153.                     return true;
  154.                 if( iLine + 1 < nsEmptyByLine.size() && nsEmptyByLine.get( iLine + 1 ).size() < nLineCells - 1 ) {
  155.                     arcFirstEnPerm( nLineCells - 1, iLine );
  156.                     arcRepack( iLine + 1 );
  157.                     return true;
  158.                 }
  159.                 if( iLine == 0 )
  160.                     return false;
  161.  
  162.                 iLine--;
  163.             }
  164.         }
  165.  
  166.         boolean isOptimal() {
  167.             return nEmpty == 0;
  168.         }
  169.  
  170.         void dump() {
  171.             char[] chs = new char[nCell + iHeight];
  172.             for( int i = 0; i < iHeight; i++ ) {
  173.                 System.arraycopy( chCells, i*iWidth, chs, i*(iWidth + 1), iWidth );
  174.                 chs[i*(iWidth + 1) + iWidth] = '\n';
  175.             }
  176.             System.out.println( new String( chs ) );
  177.         }
  178.  
  179.         private int arcLowestOccupiedLine() {
  180.             for( int i = nsEmptyByLine.size() - 1; i >= 0; i-- )
  181.                 if( !nsEmptyByLine.get( i ).isEmpty() )
  182.                     return i;
  183.  
  184.             throw new InternalError();
  185.         }
  186.  
  187.         private List<Integer> arcReservableCellsForLine( int iLine ) {
  188.             return iLine == 0 ? RESERVE_ALL : nsEmptyByLine.get( iLine - 1 );
  189.         }
  190.  
  191.         private boolean arcNextPermForLine( int iLine ) {
  192.             List<Integer> iLineCells = nsEmptyByLine.get( iLine ),
  193.                 iReservables = arcReservableCellsForLine( iLine );
  194.  
  195.             int iLim = iReservables.size();
  196.             for( int i = iLineCells.size() - 1; i >= 0; i-- ) {
  197.                 int iCell = Collections.binarySearch( iReservables, iLineCells.get( i ) );
  198.  
  199.                 if( iCell < iLim - 1 ) {
  200.                     for( int j = 0; j < iLineCells.size() - i; j++ )
  201.                         iLineCells.set( i + j, iReservables.get( iCell + 1 + j ) );
  202.  
  203.                     arcSyncLine( iLine );
  204.                     arcRepack( iLine + 1 );
  205.                     return true;
  206.                 }
  207.                 iLim = iCell;
  208.             }
  209.             return false;
  210.         }
  211.  
  212.         private void arcRepack( int iFirstLine ) {
  213.             int nOutstanding = nEmpty,
  214.                 nRepackable = arcReservableCellsForLine( iFirstLine ).size();
  215.  
  216.             for( int i = 0; i < iFirstLine; i++ )
  217.                 nOutstanding -= nsEmptyByLine.get( i ).size();
  218.  
  219.             for( int iLine = iFirstLine; iLine < nsEmptyByLine.size(); iLine++ ) {
  220.                 int nPack = Math.min( nRepackable, nOutstanding );
  221.  
  222.                 arcFirstEnPerm( nPack, iLine );
  223.                 nOutstanding -= nPack;
  224.             }
  225.         }
  226.  
  227.         private void arcFirstEnPerm( int n, int iLine ) {
  228.             List<Integer> iLineCells = nsEmptyByLine.get( iLine ),
  229.                 iReservables = arcReservableCellsForLine( iLine );
  230.  
  231.             iLineCells.clear();
  232.             iLineCells.addAll( iReservables.subList( 0, n ) );
  233.             arcSyncLine( iLine );
  234.         }
  235.  
  236.         private void arcSyncLine( int iLine ) {
  237.             Arrays.fill( chCells, iLine*iWidth, (iLine + 1)*iWidth, CLEAR_CELL );
  238.             for( int i : nsEmptyByLine.get( iLine ) )
  239.                 chCells[iLine*iWidth + i] = RESERVED_EMPTY_CELL;
  240.         }
  241.  
  242.         int firstEmptyCell( int i0 ) {
  243.             for( int i = i0; i < nCell; i++ )
  244.                 if( chCells[i] == CLEAR_CELL )
  245.                     return i;
  246.  
  247.             throw new InternalError();
  248.         }
  249.  
  250.         void addPieceInFrame( StackFrame SF ) {
  251.             fillPieceCells( SF.PaO.Pc.iCells[SF.PaO.iOrient], SF.iCell, SF.PaO.Pc.chRep );
  252.             SF.load();
  253.         }
  254.  
  255.         void subtractPieceInFrame( StackFrame SF ) {
  256.             fillPieceCells( SF.PaO.Pc.iCells[SF.PaO.iOrient], SF.iCell, CLEAR_CELL );
  257.             SF.unload();
  258.         }
  259.  
  260.         boolean canAddPieceInFrame( StackFrame SF ) {
  261.             PieceAndOrientation PaO = SF.PaO;
  262.             int[] iBounds = PaO.Pc.iBounds[PaO.iOrient];
  263.  
  264.             int iX = SF.iCell %iWidth,
  265.                 iY = SF.iCell/iWidth;
  266.  
  267.             if( iX + iBounds[0] < 0 || iX + iBounds[1] > iWidth || iY + iBounds[2] > iHeight )
  268.                 return false;
  269.  
  270.             int[][] isCells = PaO.Pc.iCells[PaO.iOrient];
  271.             for( int[] iCells : isCells )
  272.                 if( chCells[SF.iCell + iCells[1]*iWidth + iCells[0]] != CLEAR_CELL )
  273.                     return false;
  274.  
  275.             fillPieceCells( isCells, SF.iCell, PLACEHOLDER_CELL );
  276.  
  277.             int iX0 = Math.max( iX + iBounds[0] - 1, 0 ),
  278.                 iX1 = Math.min( iX + iBounds[1], iWidth );
  279.  
  280.             /* The following gap-finding routine is optimized for OMINO <= 5.  It will erroneously detect gaps for
  281.                larger polyominos.  A more general gap-finder would examine all cells in the
  282.                range [x0 <= x < x1, Y <= y < Y+B[2]] and report a gap iff an empty cell is found without a 4-connected
  283.                path to the highest-index unoccupied cell in chCells. */
  284.  
  285.             assert OMINO <= 5 + /* keep the IDE happy */ Math.min(0,1);
  286.  
  287.             boolean hasGap = false;
  288.             for( int iY_ = iY; iY_ < iY + iBounds[2]; iY_++ )
  289.                 for( int iX_ = iX0; iX_ < iX1; iX_++ )
  290.                     if( chCells[iY_*iWidth + iX_] == CLEAR_CELL ) {
  291.                         if( iX_ < iWidth - 1 && chCells[iY_*iWidth + iX_ + 1] == CLEAR_CELL )
  292.                             continue;
  293.                         if( iY_ < iHeight - 1 && chCells[(iY_ + 1)*iWidth + iX_] == CLEAR_CELL )
  294.                             continue;
  295.                         if( iX_ > 0 && iY_ < iHeight - 1 && chCells[iY_*iWidth + iX_ - 1] == CLEAR_CELL &&
  296.                             chCells[(iY_ + 1)*iWidth + iX_ - 1] == CLEAR_CELL )
  297.                         {
  298.                             continue;
  299.                         }
  300.  
  301.                         hasGap = true;
  302.                         break;
  303.                     }
  304.  
  305.             fillPieceCells( isCells, SF.iCell, CLEAR_CELL );
  306.  
  307.             return !hasGap;
  308.         }
  309.  
  310.         private void fillPieceCells( int[][] isCells, int i0, char ch ) {
  311.             for( int[] iCells : isCells )
  312.                 chCells[i0 + iCells[1]*iWidth + iCells[0]] = ch;
  313.         }
  314.     }
  315.  
  316.  
  317.     private static final class StackFrame {
  318.         final EnumMap<Piece,Integer> M_Pcs;
  319.         final Iterator<Piece> I_Pcs;
  320.         final int iCell;
  321.  
  322.         PieceAndOrientation PaO;
  323.         boolean isLoaded;
  324.  
  325.         StackFrame( EnumMap<Piece,Integer> M_Pcs, int iCell ) {
  326.             this.M_Pcs = M_Pcs;
  327.             this.iCell = iCell;
  328.  
  329.             I_Pcs = byFrequencyIterator( M_Pcs );
  330.             PaO = new PieceAndOrientation( null, -1 );
  331.             isLoaded = false;
  332.         }
  333.  
  334.         boolean isPieceLoaded() {
  335.             return isLoaded;
  336.         }
  337.  
  338.         boolean advance() {
  339.             Piece Pc = PaO.Pc;
  340.             int iOrient = PaO.iOrient;
  341.  
  342.             if( iOrient == -1 || iOrient == Pc.nOrient - 1 ) {
  343.                 if( !I_Pcs.hasNext() )
  344.                     return false;
  345.  
  346.                 Pc = I_Pcs.next();
  347.                 iOrient = 0;
  348.             } else iOrient++;
  349.  
  350.             PaO = new PieceAndOrientation( Pc, iOrient );
  351.             return true;
  352.         }
  353.  
  354.         StackFrame withoutCurrentPiece( int iNewCell ) {
  355.             EnumMap<Piece,Integer> M_NewPcs = M_Pcs.clone();
  356.  
  357.             int n = M_NewPcs.get( PaO.Pc );
  358.             if( n == 1 )
  359.                 M_NewPcs.remove( PaO.Pc );
  360.             else M_NewPcs.put( PaO.Pc, n - 1 );
  361.  
  362.             return new StackFrame( M_NewPcs, iNewCell );
  363.         }
  364.  
  365.         void load() {
  366.             assert !isLoaded;
  367.             isLoaded = true;
  368.         }
  369.  
  370.         void unload() {
  371.             assert isLoaded;
  372.             isLoaded = false;
  373.         }
  374.  
  375.         static Iterator<Piece> byFrequencyIterator( EnumMap<Piece,Integer> M_ForPcs ) {
  376.             List<Piece> Pcs = new ArrayList<>( M_ForPcs.keySet() );
  377.  
  378.             Collections.sort( Pcs, (Pc1,Pc2) -> M_ForPcs.get( Pc2 ) - M_ForPcs.get( Pc1 ) );
  379.             return Pcs.iterator();
  380.         }
  381.     }
  382.  
  383.  
  384.     private static final class PieceAndOrientation {
  385.         final Piece Pc;
  386.         final int iOrient;
  387.  
  388.         PieceAndOrientation( Piece Pc, int iOrient ) {
  389.             this.Pc = Pc;
  390.             this.iOrient = iOrient;
  391.         }
  392.     }
  393.  
  394.  
  395.     enum Piece {
  396.         T( 'T', new int[][] { { -1, 2, 2 }, { 0, 2, 3 }, { 0, 3, 2 }, { -1, 1, 3 } },
  397.             new int[][][] {
  398.                 { { 0, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 } },
  399.                 { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 0, 2 } },
  400.                 { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 1, 1 } },
  401.                 { { 0, 0 }, { -1, 1 }, { 0, 1 }, { 0, 2 } }
  402.             } ),
  403.  
  404.         I( 'I', new int[][] { { 0, 4, 1 }, { 0, 1, 4 } },
  405.             new int[][][] {
  406.                 { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 } },
  407.                 { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 } }
  408.             } ),
  409.  
  410.         J( 'J', new int[][] { { -1, 1, 3 }, { 0, 3, 2 }, { 0, 2, 3 }, { 0, 3, 2 } },
  411.             new int[][][] {
  412.                 { { 0, 0 }, { 0, 1 }, { -1, 2 }, { 0, 2 } },
  413.                 { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 } },
  414.                 { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 0, 2 } },
  415.                 { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 2, 1 } }
  416.             } ),
  417.  
  418.         L( 'L', new int[][] { { 0, 2, 3 }, { 0, 3, 2 }, { 0, 2, 3 }, { -2, 1, 2 } },
  419.             new int[][][] {
  420.                 { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 2 } },
  421.                 { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 0, 1 } },
  422.                 { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 1, 2 } },
  423.                 { { 0, 0 }, { -2, 1 }, { -1, 1 }, { 0, 1 } }
  424.             } ),
  425.  
  426.         S( 'S', new int[][] { { -1, 2, 2 }, { 0, 2, 3 } },
  427.             new int[][][] {
  428.                 { { 0, 0 }, { 1, 0 }, { -1, 1 }, { 0, 1 } },
  429.                 { { 0, 0 }, { 0, 1 }, { 1, 1 }, { 1, 2 } }
  430.             } ),
  431.  
  432.         Z( 'Z', new int[][] { { 0, 3, 2 }, { -1, 1, 3 } },
  433.             new int[][][] {
  434.                 { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 2, 1 } },
  435.                 { { 0, 0 }, { -1, 1 }, { 0, 1 }, { -1, 2 } }
  436.             } ),
  437.  
  438.         O( 'O', new int[][] { { 0, 2, 2 } },
  439.             new int[][][] {
  440.                 { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } }
  441.             } );
  442.  
  443.         final char chRep;
  444.         final int nOrient;
  445.         final int[][] iBounds;
  446.         final int[][][] iCells;
  447.  
  448.         Piece( char chRep, int[][] iBounds, int[][][] iCells ) {
  449.             this.chRep = chRep;
  450.             this.iBounds = iBounds;
  451.             this.iCells = iCells;
  452.             nOrient = iBounds.length;
  453.         }
  454.  
  455.         static Piece fromChar( char ch ) {
  456.             for( Piece Pc : Piece.values() )
  457.                 if( Pc.chRep == ch )
  458.                     return Pc;
  459.  
  460.             throw new IllegalArgumentException( "unknown shape mnemonic: '" + ch + "'" );
  461.         }
  462.     }
  463. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement