Skip to content

Instantly share code, notes, and snippets.

@ishmaelmakitla
Created March 21, 2016 17:10
Show Gist options
  • Save ishmaelmakitla/d89d29cb27332606efa8 to your computer and use it in GitHub Desktop.
Save ishmaelmakitla/d89d29cb27332606efa8 to your computer and use it in GitHub Desktop.
package ishmael.academia.examples.tut.dpc401t;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* This is a utility class for processing Paint Commands
*
* @author Ishmael Makitla,
* Lecturer - Digital Process Control IV (DPC401T) 2016
*
*/
public class PaintRobotCommandProcessor {
private static final char DOT = '.';
private static final char HASH = '#';
private static final String SPACE = " ";
private static final String DIM = "DIM";
private static final String PAINT_LINE = "PAINT_LINE";
private static final String PAINT_SQUARE = "PAINT_SQUARE";
//this keeps list of commands that produce the output
List<String> commands = new ArrayList<String>();
private char[][] mPictureMatrix;
public PaintRobotCommandProcessor(char[][] inputMatrix){
this.mPictureMatrix = inputMatrix;
}
/**
* Helper function to process the paint command (update the character-matrix with '.' or '#')
* PAINT_SQUARE R C S - paints all cells within the square of (2S + 1) × (2S + 1) dimensions centered at [R , C ] .
In particular, the command “PAINT_SQUARE R C 0” paints a single cell [R , C ]: (0+1)x(0+1) = 1 (number of cells)
Another example: PAINT_SQUARE 2 3 1 : Centre = [2,3] : ((2x1) +1)((2x1)+1) = (3)x(3) = 9 (number of cells to paint):
..###..
..###.. (centre = Row 3 (3-1), Col 4 (4-1))
..###..
* @param paintCommand
*/
public void processPaintSquareCommand(int rowIndex, int columnIndex, int s){
int cellNumbers = ((s*2)+1)*((s*2)+1);
int cellsPerSide = (int)Math.sqrt((double)cellNumbers)/2;
//do the actual painting by changing the characters in the array that are within the specified square
if(cellNumbers == 1){
//this is just the printing of one cell - so only one character needs changing
mPictureMatrix[rowIndex][columnIndex] = HASH;
commands.add("PAINT_SQUARE"+" "+rowIndex+" "+columnIndex+" "+s);
}
else if (cellsPerSide > 1){
//this is a square bigger than 3x3
commands.add("PAINT_SQUARE"+" "+rowIndex+" "+columnIndex+" "+s);
printBigSquare(rowIndex, columnIndex, s);
}
else{
//the cellNumber has to be indivisible by 2 (otherwise we cannot have a centre)
if((cellNumbers%2 == 0)){
System.out.println("Invalid Command for Square Painting ... Number of Cells MUST NOT be divisible by 2. MUST be Odd!! ");
return;
}
//First we paint the centre - easiest
mPictureMatrix[rowIndex][columnIndex] = HASH;
//here we are painting a number of cells within a square, so we calculate the square-root of the cellNumbers -
//we then work out which cells are within the square
//we compute the length of each side of the square - by calculating the square-root (this is the number of cells making each side)
int sideLength = (int)Math.sqrt((double)cellNumbers);
//number of cells on each side of the centre
int cellsToTheEdgeOfSquare = sideLength/2;
int index = 1;
for(int x=0; x < cellsToTheEdgeOfSquare; x++){
//drawing a cross from the center to the four sides
//up
mPictureMatrix[rowIndex-index][columnIndex] = HASH;
//up-and-right
mPictureMatrix[rowIndex-index][columnIndex+index] = HASH;
//up-and-left
mPictureMatrix[rowIndex-index][columnIndex-index] = HASH;
//left
mPictureMatrix[rowIndex][columnIndex-index] = HASH;
//right
mPictureMatrix[rowIndex][columnIndex+index] = HASH;
//down
mPictureMatrix[rowIndex+index][columnIndex] = HASH;
//down-and-right
mPictureMatrix[rowIndex+index][columnIndex+index] = HASH;
//down-and-left
mPictureMatrix[rowIndex+index][columnIndex-index] = HASH;
index +=1;
}
//add the command to the command-list
//Command-String :: PAINT_SQUARE 5 65 1
commands.add("PAINT_SQUARE"+" "+rowIndex+" "+columnIndex+" "+s);
}
}
/**
* For printing square bigger than S=1
*/
private void printBigSquare(int rowIndex, int columnIndex, int s){
//draw lines of length-x starting from top-left corder of the desired square
//we compute the length of each side of the square - by calculating the square-root (this is the number of cells making each side)
int cellNumbers = ((s*2)+1)*((s*2)+1);
int sideLength = (int)Math.sqrt((double)cellNumbers);
//sideLength is the length of each line we will draw to make our square
//work out the coordinates of the first and last lines
//first we need to know how many cells to the edge of the squre
int cellsToTheEdgeOfSquare = sideLength/2;
int firstColumnIndex = columnIndex - cellsToTheEdgeOfSquare;
int lastColumnIndexEnd = columnIndex + cellsToTheEdgeOfSquare+1; //we add 1 to INCLUDE the last column
int firstRowIndex = rowIndex - cellsToTheEdgeOfSquare;
int lastRowIndex = rowIndex + cellsToTheEdgeOfSquare+1; //we add 1 to INCLUDE the last row
//loop from the start-cell of the square to the end, and move to the next row
for(int row=firstRowIndex; row < lastRowIndex; row++){
for( int col=firstColumnIndex; col < lastColumnIndexEnd; col++){
mPictureMatrix[row][col] = HASH;
}
}
}
/**
* PAINT_LINE R1 C1 R2 C2 - paints all cells in a horizontal or vertical run between [R1, C1] (start) and [R2,C2] (end).
* That is, at least one of the two has to be true: R1 = R2 or/and C1 = C2 .
*/
public void processPaintLineCommand(int startRowIndex, int startColumnIndex, int endRowIndex, int endColumnIndex){
//since this is a line, either same column or same row - otherwise the command is invalid
//E.g line on row-0 starts at 0,0 ends at 0,5 (horizontal), or starts at 0,0 ends at 5,0 (vertical)
if( (startRowIndex) != endRowIndex && (startColumnIndex != endColumnIndex)){
System.out.println("Invalid Command for Line Painting ... ");
return;
}
boolean isHorizontal = (startRowIndex == endRowIndex ? true : false);
//draw the line of length
if(isHorizontal){
for(int x= startColumnIndex; x < endColumnIndex+1; x++){
mPictureMatrix[startRowIndex][x] = HASH;
}
}
else{
for(int x= startRowIndex; x < endRowIndex+1; x++){
mPictureMatrix[x][startColumnIndex] = HASH;
}
}
//add the Paint-Line command of the form: PAINT_LINE 0 4 3 4
commands.add("PAINT_LINE"+" "+startRowIndex+" "+startColumnIndex+" "+endRowIndex+" "+endColumnIndex);
}
/**
* Method to process the ERASE command: it basically puts '.' at the specified cell of the 2-D array;
* ERASE_CELL R C - clears the cell [R,C].
*
*/
public void processEraseCellCommand(int rowIndex, int columnIndex){
mPictureMatrix[rowIndex][columnIndex] = DOT;
//add Erase Cell command of the form: ERASE_CELL 8 35
commands.add("ERASE_CELL"+" "+rowIndex+" "+columnIndex);
}
public String showAsString(){
return asStringPictureMatrix(mPictureMatrix);
}
/**
* Convenience method to create a picture-matrix from dimensions
*/
public static char[][] createPictureMatrix(final int rows, final int columns){
char[][] pictureMatrix = new char[rows][columns];
for(int x=0; x< rows; x++){
for (int y=0; y <columns; y++){
pictureMatrix[x][y] = DOT;
}
}
return pictureMatrix;
}
/**
* Convenience method that converts the array onto a matrix-string. It is used to generate the output
* similar to those submitted by Google for Hash Code Practice Challenge;
*
*/
public static String asStringPictureMatrix(final char[][] pictureMatrix){
int rows = pictureMatrix.length;
int columns = pictureMatrix[0].length;
String outputMatrix = "";
StringBuilder stringBuilder = new StringBuilder();
for(int x=0; x< rows; x++){
for (int y=0; y <columns; y++){
stringBuilder.append(pictureMatrix[x][y]);
//end of row?
if(y+1 >= columns){
stringBuilder.append('\n');
}
}
}
outputMatrix = stringBuilder.toString();
return outputMatrix;
}
/**
* This function is used to create a file of commands used to produce the desired outcomes
*/
public void createCommandsFile(){
try {
FileWriter fw = new FileWriter("commands.txt");
for (int x = 0; x < commands.size(); x++) {
fw.write(commands.get(x));
fw.write('\n');
}
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* This utility function is used to read commands from the file and process them in the sequence they appear in the commands-file.
* The first line of the file should be the directive for the dimentions DIM Rows Columns
* The expected line format is:
* PAINT_SQUARE 18 69 5
* PAINT_LINE 20 52 20 63
*/
public static void processCommandFile(String commandFilePath) throws IllegalArgumentException{
int dimRows = 0;
int dimColumns = 0;
PaintRobotCommandProcessor processorInstance = null;
try{
FileReader fileReaderr = new FileReader(commandFilePath);
BufferedReader bufferedReader = new BufferedReader(fileReaderr);
String commandLine;
int lineCounter = 0;
while ((commandLine = bufferedReader.readLine()) != null){
//if this is the first line, then it specified the dimensions - these will be used to make sure
//the painted cells are within range
if(lineCounter == 0){
//the first word must be DIM, followed by rows and then columns -
String[] dimensionWords = commandLine.split(SPACE);
if(dimensionWords.length < 3){
throw new IllegalArgumentException("Invalid Format for dimension directive. Should be three words: DIM Num Num");
}
if(dimensionWords[0].trim().equalsIgnoreCase(DIM)){
dimRows = Integer.parseInt(dimensionWords[1]);
dimColumns = Integer.parseInt(dimensionWords[2]);
char[][] inputMatrix = createPictureMatrix(dimRows, dimColumns);
processorInstance = new PaintRobotCommandProcessor(inputMatrix);
}
else{
//invalid entry in the file
throw new IllegalArgumentException("Invalid Command In the File. Expeceted dimension directive DIM");
}
}
else{
//this is the subsequent command line
String[] paintCommandWords = commandLine.split(SPACE);
if(paintCommandWords.length < 4 || paintCommandWords.length > 5){
throw new IllegalArgumentException("Invalid Format for Paint Command. Should be Four or Five Words");
}
String commandType = paintCommandWords[0];
if(commandType!= null && commandType.trim().equalsIgnoreCase(PAINT_LINE)){
//command format: PAINT_LINE 20 52 20 63
int startCellRow = Integer.parseInt(paintCommandWords[1]);
int startCellColumn = Integer.parseInt(paintCommandWords[2]);
int endCellRow = Integer.parseInt(paintCommandWords[3]);
int endCellColumn = Integer.parseInt(paintCommandWords[4]);
//For simulation, we call the processPaintLineCommand
processorInstance.processPaintLineCommand(startCellRow, startCellColumn, endCellRow, endCellColumn);
//ideally, at this point you should send the command to your paint-robot
}
else if (commandType!= null && commandType.trim().equalsIgnoreCase(PAINT_SQUARE)){
//command format: PAINT_SQUARE 18 69 5
int squareCentreCellRow = Integer.parseInt(paintCommandWords[1]);
int squareCentreCellColumn = Integer.parseInt(paintCommandWords[2]);
int sideLength = Integer.parseInt(paintCommandWords[3]);
//For simulation, we call the processPaintSquareCommand on our simulator
processorInstance.processPaintSquareCommand(squareCentreCellRow, squareCentreCellColumn, sideLength);
}
}
lineCounter +=1;
}
//if we did create the processor instance, we can try printing it out
if(processorInstance != null){
System.out.println(processorInstance.showAsString());
}
}
catch(Exception commandProcessingException){
throw new IllegalArgumentException("There was a problem processing Commands File. Message : "+commandProcessingException.getLocalizedMessage());
}
}
}
@ishmaelmakitla
Copy link
Author

Here is how to use the file in your application:
String commandFile = "commands.txt"; // this is the file that contains the commands... PaintRobotCommandProcessor.processCommandFile(commandFile);

@ishmaelmakitla
Copy link
Author

Here is an example of the content of the command.txt file:
DIM 20 20
PAINT_SQUARE 10 10 3
PAINT_LINE 0 0 0 19
PAINT_LINE 1 0 1 19
PAINT_LINE 10 17 19 17
PAINT_LINE 10 19 19 19

Copy and paste the following into a file called commands.txt in the working directory of your project - then you can use the command-processor as illustrated in the comment above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment