import java.io.BufferedReader; import java.io.FileReader; import java.io.InputStreamReader; import java.util.StringTokenizer; /* * RubiksCube.java * * Created on September 18, 2002, 7:24 PM */ /** * This class is an abstract representation of a rubiks cube. It is implemented * by using 6 Face objects. * * @author andrewm */ public class RubiksCube { // Constants representing each of the faces public static final String FRONT = "front"; public static final String BACK = "back"; public static final String TOP = "top"; public static final String BOTTOM = "bottom"; public static final String LEFT = "left"; public static final String RIGHT = "right"; // Output constants public static final String SUCCESS = "Yes"; public static final String FAILURE = "No"; // Input constants public static final String START = "START"; public static final String END = "END"; public static final String END_OF_INPUT = "ENDOFINPUT"; // Face objects private Face front; private Face back; private Face top; private Face bottom; private Face left; private Face right; /** * Public constructor */ public RubiksCube() { front = new Face(); back = new Face(); top = new Face(); bottom = new Face(); left = new Face(); right = new Face(); } /** * Populates the cube from the BufferedReader passed in * * @param in BufferedReader used for reading in data * @throws Exception */ public void populate( BufferedReader in ) throws Exception { // StringTokenizer used for parsing data StringTokenizer st = null; // character arrays used for holding input char[] _front = new char[9]; char[] _back = new char[9]; char[] _top = new char[9]; char[] _bottom = new char[9]; char[] _left = new char[9]; char[] _right = new char[9]; try { // read in the values for the top surface for( int i = 0; i < 9; i += 3 ) { st = new StringTokenizer( in.readLine() ); if ( st.countTokens() != 3 ) { throw new Exception( "invalid number of data values" ); } _top[i] = st.nextToken().charAt( 0 ); _top[i + 1] = st.nextToken().charAt( 0 ); _top[i + 2] = st.nextToken().charAt( 0 ); } top.setData( _top ); // read in the left, front, right and back surfaces for( int i = 0; i < 9; i += 3 ) { st = new StringTokenizer( in.readLine() ); if ( st.countTokens() != 12 ) { throw new Exception( "invalid number of data values" ); } _left[i] = st.nextToken().charAt( 0 ); _left[i + 1] = st.nextToken().charAt( 0 ); _left[i + 2] = st.nextToken().charAt( 0 ); _front[i] = st.nextToken().charAt( 0 ); _front[i + 1] = st.nextToken().charAt( 0 ); _front[i + 2] = st.nextToken().charAt( 0 ); _right[i] = st.nextToken().charAt( 0 ); _right[i + 1] = st.nextToken().charAt( 0 ); _right[i + 2] = st.nextToken().charAt( 0 ); _back[i] = st.nextToken().charAt( 0 ); _back[i + 1] = st.nextToken().charAt( 0 ); _back[i + 2] = st.nextToken().charAt( 0 ); } left.setData( _left ); front.setData( _front ); right.setData( _right ); back.setData( _back ); // read in the values for the bottom surface for( int i = 0; i < 9; i += 3 ) { st = new StringTokenizer( in.readLine() ); if ( st.countTokens() != 3 ) { throw new Exception( "invalid number of data values" ); } _bottom[i] = st.nextToken().charAt( 0 ); _bottom[i + 1] = st.nextToken().charAt( 0 ); _bottom[i + 2] = st.nextToken().charAt( 0 ); } bottom.setData( _bottom ); } catch( Exception e ) { System.out.println( "Error in populate method. Possibly due to invalid input" ); throw e; } } /** * more - Performs a movement on the rubiks cube * * @param command String representation of the command * @throws Exception */ public void move( String command ) throws Exception { String face = null; String direction = null; StringTokenizer st = null; char[] temp = new char[3]; st = new StringTokenizer( command ); if ( st.countTokens() == 2 ) { // parse out the face and the direction face = st.nextToken(); direction = st.nextToken(); if ( face.equals( FRONT ) ) { if ( direction.equals( RIGHT ) ) { front.rotate( Face.CLOCKWISE ); System.arraycopy( top.getTriplet( Face.BOTTOM ), 0, temp, 0, 3 ); top.updateTriplet( left.getTriplet( Face.RIGHT, true ), Face.BOTTOM ); left.updateTriplet( bottom.getTriplet( Face.TOP ), Face.RIGHT ); bottom.updateTriplet( right.getTriplet( Face.LEFT, true ), Face.TOP ); right.updateTriplet( temp, Face.LEFT ); } else if ( direction.equals( LEFT ) ) { front.rotate( Face.COUNTERCLOCKWISE ); System.arraycopy( top.getTriplet( Face.BOTTOM, true ), 0, temp, 0, 3 ); top.updateTriplet( right.getTriplet( Face.LEFT ), Face.BOTTOM ); right.updateTriplet( bottom.getTriplet( Face.TOP, true ), Face.LEFT ); bottom.updateTriplet( left.getTriplet( Face.RIGHT ), Face.TOP ); left.updateTriplet( temp, Face.RIGHT ); } else { throw new Exception( "invalid rotation direction" ); } } else if ( face.equals( BACK ) ) { if ( direction.equals( RIGHT ) ) { back.rotate( Face.CLOCKWISE ); System.arraycopy( top.getTriplet( Face.TOP, true ), 0, temp, 0, 3 ); top.updateTriplet( right.getTriplet( Face.RIGHT ), Face.TOP ); right.updateTriplet( bottom.getTriplet( Face.BOTTOM, true ), Face.RIGHT ); bottom.updateTriplet( left.getTriplet( Face.LEFT ), Face.BOTTOM ); left.updateTriplet( temp, Face.LEFT ); } else if ( direction.equals( LEFT ) ) { back.rotate( Face.COUNTERCLOCKWISE ); System.arraycopy( top.getTriplet( Face.TOP ), 0, temp, 0, 3 ); top.updateTriplet( left.getTriplet( Face.LEFT, true ), Face.TOP ); left.updateTriplet( bottom.getTriplet( Face.BOTTOM ), Face.LEFT ); bottom.updateTriplet( right.getTriplet( Face.RIGHT, true ), Face.BOTTOM ); right.updateTriplet( temp, Face.RIGHT ); } else { throw new Exception( "invalid rotation direction" ); } } else if ( face.equals( TOP ) ) { if ( direction.equals( RIGHT ) ) { top.rotate( Face.CLOCKWISE ); System.arraycopy( front.getTriplet( Face.TOP ), 0, temp, 0, 3 ); front.updateTriplet( right.getTriplet( Face.TOP ), Face.TOP ); right.updateTriplet( back.getTriplet( Face.TOP ), Face.TOP ); back.updateTriplet( left.getTriplet( Face.TOP ), Face.TOP ); left.updateTriplet( temp, Face.TOP ); } else if ( direction.equals( LEFT ) ) { top.rotate( Face.COUNTERCLOCKWISE ); System.arraycopy( front.getTriplet( Face.TOP ), 0, temp, 0, 3 ); front.updateTriplet( left.getTriplet( Face.TOP ), Face.TOP ); left.updateTriplet( back.getTriplet( Face.TOP ), Face.TOP ); back.updateTriplet( right.getTriplet( Face.TOP ), Face.TOP ); right.updateTriplet( temp, Face.TOP ); } else { throw new Exception( "invalid rotation direction" ); } } else if ( face.equals( BOTTOM ) ) { if ( direction.equals( RIGHT ) ) { bottom.rotate( Face.CLOCKWISE ); System.arraycopy( front.getTriplet( Face.BOTTOM ), 0, temp, 0, 3 ); front.updateTriplet( left.getTriplet( Face.BOTTOM ), Face.BOTTOM ); left.updateTriplet( back.getTriplet( Face.BOTTOM ), Face.BOTTOM ); back.updateTriplet( right.getTriplet( Face.BOTTOM ), Face.BOTTOM ); right.updateTriplet( temp, Face.BOTTOM ); } else if ( direction.equals( LEFT ) ) { bottom.rotate( Face.COUNTERCLOCKWISE ); System.arraycopy( front.getTriplet( Face.BOTTOM ), 0, temp, 0, 3 ); front.updateTriplet( right.getTriplet( Face.BOTTOM ), Face.BOTTOM ); right.updateTriplet( back.getTriplet( Face.BOTTOM ), Face.BOTTOM ); back.updateTriplet( left.getTriplet( Face.BOTTOM ), Face.BOTTOM ); left.updateTriplet( temp, Face.BOTTOM ); } else { throw new Exception( "invalid rotation direction" ); } } else if ( face.equals( LEFT ) ) { if ( direction.equals( RIGHT ) ) { left.rotate( Face.CLOCKWISE ); System.arraycopy( front.getTriplet( Face.LEFT ), 0, temp, 0, 3 ); front.updateTriplet( top.getTriplet( Face.LEFT ), Face.LEFT ); top.updateTriplet( back.getTriplet( Face.RIGHT, true ), Face.LEFT ); back.updateTriplet( bottom.getTriplet( Face.LEFT, true ), Face.RIGHT ); bottom.updateTriplet( temp, Face.LEFT ); } else if ( direction.equals( LEFT ) ) { left.rotate( Face.COUNTERCLOCKWISE ); System.arraycopy( front.getTriplet( Face.LEFT ), 0, temp, 0, 3 ); front.updateTriplet( bottom.getTriplet( Face.LEFT ), Face.LEFT ); bottom.updateTriplet( back.getTriplet( Face.RIGHT, true ), Face.LEFT ); back.updateTriplet( top.getTriplet( Face.LEFT, true ), Face.RIGHT ); top.updateTriplet( temp, Face.LEFT ); } else { throw new Exception( "invalid rotation direction" ); } } else if ( face.equals( RIGHT ) ) { if ( direction.equals( RIGHT ) ) { right.rotate( Face.CLOCKWISE ); System.arraycopy( front.getTriplet( Face.RIGHT ), 0, temp, 0, 3 ); front.updateTriplet( bottom.getTriplet( Face.RIGHT ), Face.RIGHT ); bottom.updateTriplet( back.getTriplet( Face.LEFT, true ), Face.RIGHT ); back.updateTriplet( top.getTriplet( Face.RIGHT, true ), Face.LEFT ); top.updateTriplet( temp, Face.RIGHT ); } else if ( direction.equals( LEFT ) ) { right.rotate( Face.COUNTERCLOCKWISE ); System.arraycopy( front.getTriplet( Face.RIGHT ), 0, temp, 0, 3 ); front.updateTriplet( top.getTriplet( Face.RIGHT ), Face.RIGHT ); top.updateTriplet( back.getTriplet( Face.LEFT, true ), Face.RIGHT ); back.updateTriplet( bottom.getTriplet( Face.RIGHT, true ), Face.LEFT ); bottom.updateTriplet( temp, Face.RIGHT ); } else { throw new Exception( "invalid rotation direction" ); } } else { throw new Exception( "invalid cube face" ); } } else { throw new Exception( "invalid command: " + command ); } } /** * Prints out the current representation of the cube */ public void print() { // print the top face System.out.println( " " + top.getRow( Face.TOP ) ); System.out.println( " " + top.getRow( Face.MIDDLE ) ); System.out.println( " " + top.getRow( Face.BOTTOM ) ); // print the first row of the left, front, right and back faces System.out.print( left.getRow( Face.TOP ) + " " + front.getRow( Face.TOP ) + " " ); System.out.println( right.getRow( Face.TOP ) + " " + back.getRow( Face.TOP ) ); // print the second row of the left, front, right and back faces System.out.print( left.getRow( Face.MIDDLE ) + " " + front.getRow( Face.MIDDLE ) + " " ); System.out.println( right.getRow( Face.MIDDLE ) + " " + back.getRow( Face.MIDDLE ) ); // print the third row of the left, front, right and back faces System.out.print( left.getRow( Face.BOTTOM ) + " " + front.getRow( Face.BOTTOM ) + " " ); System.out.println( right.getRow( Face.BOTTOM ) + " " + back.getRow( Face.BOTTOM ) ); // print the bottom face System.out.println( " " + bottom.getRow( Face.TOP ) ); System.out.println( " " + bottom.getRow( Face.MIDDLE ) ); System.out.println( " " + bottom.getRow( Face.BOTTOM ) ); } /** * checks to see if the cube is solved * * @return boolean Returns true if the cube is solved */ public boolean isSolved() { return ( ( front.isComplete() ) && ( back.isComplete() ) && ( top.isComplete() ) && ( bottom.isComplete() ) && ( left.isComplete() ) && ( right.isComplete() ) ); } /** * Main method which is run from the command line */ public static void main( String args[] ) { String line = null; BufferedReader in = null; RubiksCube cube = null; try { // open the input stream in = new BufferedReader( new InputStreamReader( System.in ) ); // read in a line line = in.readLine(); // while there is still more input while( ( line != null ) && ( !"".equals( line ) ) ) { // check to see if it starts with the correct start symbol if ( line.startsWith( START ) ) { // create a new cube cube = new RubiksCube(); // populate the cube cube.populate( in ); // read in a line of data line = in.readLine(); // loop through all of the movement commands while( ( line != null ) && ( !"".equals( line ) ) ) { // check for the end of the data set if ( line.startsWith( END ) ) { break; } // send the command to the move method cube.move( line ); // read in the next line line = in.readLine(); } } else if ( line.startsWith( END_OF_INPUT ) ) { break; } else { throw new Exception( "Invalid start symbol. Expecting " + START ); } // check to see if the cube is solved if ( cube.isSolved() ) { System.out.println( SUCCESS ); } else { System.out.println( FAILURE ); } line = in.readLine(); } } catch( Exception e ) { e.printStackTrace(); System.out.println( e.getMessage() ); } } } /** * This class is an abstract representation of a rubiks cube face */ class Face { // the actual data of the face private char[] data; // constants used for the direction of rotations public static final byte CLOCKWISE = 0; public static final byte COUNTERCLOCKWISE = 1; // constants used to signify a row or column of data public static final byte TOP = 0; public static final byte MIDDLE = 1; public static final byte BOTTOM = 2; public static final byte LEFT = 3; public static final byte RIGHT = 4; /** * public constructor */ public Face() { data = new char[9]; } /** * gets a row or column of data from this face * * @param edge A byte representing which edge to get * @return char[] Character array containing the data */ public char[] getTriplet( byte edge ) { return getTriplet( edge, false ); } /** * Gets a row or column of data of this face * * @param edge A byte representing the edge to get * @param reverse A boolean indicating whether or not the data should be reversed * @return char[] Character array containing the data */ public char[] getTriplet( byte edge, boolean reverse ) { char[] temp = new char[3]; switch( edge ) { case TOP : if ( reverse ) { temp[0] = data[2]; temp[1] = data[1]; temp[2] = data[0]; } else { temp[0] = data[0]; temp[1] = data[1]; temp[2] = data[2]; } break; case BOTTOM : if ( reverse ) { temp[0] = data[4]; temp[1] = data[5]; temp[2] = data[6]; } else { temp[0] = data[6]; temp[1] = data[5]; temp[2] = data[4]; } break; case LEFT : if ( reverse ) { temp[0] = data[6]; temp[1] = data[7]; temp[2] = data[0]; } else { temp[0] = data[0]; temp[1] = data[7]; temp[2] = data[6]; } break; case RIGHT : if ( reverse ) { temp[0] = data[4]; temp[1] = data[3]; temp[2] = data[2]; } else { temp[0] = data[2]; temp[1] = data[3]; temp[2] = data[4]; } break; } return temp; } /** * Updates a row or column of the face depending on the edge and direction * * @param edge The new data to be put in the row or column * @param byte Byte representing the edge to replace */ public void updateTriplet( char[] edge, byte direction ) { switch( direction ) { case TOP: data[0] = edge[0]; data[1] = edge[1]; data[2] = edge[2]; break; case BOTTOM: data[6] = edge[0]; data[5] = edge[1]; data[4] = edge[2]; break; case LEFT: data[0] = edge[0]; data[7] = edge[1]; data[6] = edge[2]; break; case RIGHT: data[2] = edge[0]; data[3] = edge[1]; data[4] = edge[2]; break; } } /** * getRow - returns a string representation of a data row. * * @param row A byte representing which row to return * @return String a string representation of a row of data */ public String getRow( byte row ) { String result = "invalid row"; switch( row ) { case TOP: result = data[0] + " " + data[1] + " " + data[2]; break; case MIDDLE: result = data[7] + " " + data[8] + " " + data[3]; break; case BOTTOM: result = data[6] + " " + data[5] + " " + data[4]; break; } return result; } /** * method to check whether or not this face is complete. * * @return boolean Returns true if the face is complete */ public boolean isComplete() { boolean complete = true; char color = data[data.length - 1]; for( int i = 0; i < data.length; i++ ) { if ( data[i] != color ) { complete = false; break; } } return complete; } /** * rotate - performs a rotation on the data which simulates turning the face * around the center square. * * @param byte A byte representing the direction of the rotation */ public void rotate( byte direction ) { char temp; if ( direction == CLOCKWISE ) { // teporarily store the top left corner temp = data[6]; // rotate the corner squares for( int i = 6; i >= 2; i -= 2 ) { data[i] = data[i - 2]; } // copy the temp into the top right corner data[0] = temp; // temporarily hold the left side edge temp = data[7]; // rotate the edge squares for( int i = 7; i >= 3; i -= 2 ) { data[i] = data[i - 2]; } // copy the temp to the top edge data[1] = temp; } else if ( direction == COUNTERCLOCKWISE ) { // temporarily store the top left corner temp = data[0]; // rotate the corner squares for( int i = 0; i <= 4; i += 2 ) { data[i] = data[i + 2]; } // copy the temp to the bottom left corner data[6] = temp; // temporarily store the top edge temp = data[1]; // rotate the edge squares for( int i = 1; i <= 5; i += 2 ) { data[i] = data[i + 2]; } // copy the temp to the left edge data[7] = temp; } } /** * Sets the data array for this face instance * * @param data Character array containing the new data * @throws Exception Thrown if the input data isn't the correct length */ public void setData( char[] _data ) throws Exception { if ( _data.length == 9 ) { data[0] = _data[0]; data[1] = _data[1]; data[2] = _data[2]; data[3] = _data[5]; data[4] = _data[8]; data[5] = _data[7]; data[6] = _data[6]; data[7] = _data[3]; data[8] = _data[4]; } else { throw new Exception( "Invalid input into setData method" ); } } }