diff --git a/app/build.gradle b/app/build.gradle index a370bee..fd23833 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,9 @@ plugins { apply plugin: "androidx.navigation.safeargs" +//This is needed for SVG->PNG +//apply plugin: 'com.trello.victor' + // // Convert SVG images from art directory into PNG files. // @@ -19,7 +22,7 @@ apply plugin: "androidx.navigation.safeargs" //} //preBuild.dependsOn generateDrawablesFromArt -String gitVersion() { +static String gitVersion() { def versionP = 'git describe --tags --long --dirty=-x --always --abbrev=8' .execute() versionP.waitFor() @@ -28,15 +31,15 @@ String gitVersion() { return versionP.text.trim() } -String gitHash() { - def githashP = 'git rev-parse HEAD'.execute() - githashP.waitFor() - if (githashP.exitValue()) +static String gitHash() { + def hashP = 'git rev-parse HEAD'.execute() + hashP.waitFor() + if (hashP.exitValue()) throw new GradleException("Couldn't extract git_hash. Git exited unexpectedly.") - return githashP.text.trim() + return hashP.text.trim() } -Integer gitVersionCode() { +static Integer gitVersionCode() { def gitP = 'git rev-list v2..HEAD --count'.execute() gitP.waitFor() if (gitP.exitValue()) @@ -44,19 +47,26 @@ Integer gitVersionCode() { return gitP.text.trim().toInteger() } -String buildDate() { - def builddate = new Date() - return builddate.toString() +static String buildDate() { + def date = new Date() + return date.toString() } android { signingConfigs { release { - storeFile file('/Users/vahagnk/devel/banvor/keys/signing_key') + storeFile file('keys/signing_key') + keyAlias 'key0' } } compileSdk 31 +// sourceSets { +// main { +// svg.srcDir 'src/main/art' +// } +// } + defaultConfig { applicationId "org.vostan.banvor" minSdk 21 @@ -106,6 +116,8 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + + // This is for unit tests. testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' diff --git a/app/src/main/java/org/vostan/banvor/PuzzleBoardFragment.java b/app/src/main/java/org/vostan/banvor/PuzzleBoardFragment.java index f926159..6ed7d2a 100644 --- a/app/src/main/java/org/vostan/banvor/PuzzleBoardFragment.java +++ b/app/src/main/java/org/vostan/banvor/PuzzleBoardFragment.java @@ -61,23 +61,23 @@ public class PuzzleBoardFragment extends Fragment public void onTouch() { } }); - binding.btnTest.setOnClickListener((View.OnClickListener) view4 -> { + binding.btnTest.setOnClickListener((View.OnClickListener) v -> { createNextLevelDialog((d, w) -> { initAndShowCurrentPuzzle(); }).show(); }); - binding.btnPrev.setOnClickListener((View.OnClickListener) view1 -> { + binding.btnPrev.setOnClickListener((View.OnClickListener) v -> { gameState.setCurrentLevel(gameState.getCurrentLevel()-1); PuzzleBoardFragment.this.initAndShowCurrentPuzzle(); }); - binding.btnNext.setOnClickListener((View.OnClickListener) view2 -> { + binding.btnNext.setOnClickListener((View.OnClickListener) v -> { gameState.setCurrentLevel(gameState.getCurrentLevel()+1); PuzzleBoardFragment.this.initAndShowCurrentPuzzle(); }); - binding.btnReset.setOnClickListener((View.OnClickListener) view3 -> { + binding.btnReset.setOnClickListener((View.OnClickListener) v -> { PuzzleBoardFragment.this.initAndShowCurrentPuzzle(); }); - binding.btnUndo.setOnClickListener((View.OnClickListener) view4 -> { + binding.btnUndo.setOnClickListener((View.OnClickListener) v -> { createNextLevelDialog((d, w) -> { gameState.advanceCurrentLevel(); initAndShowCurrentPuzzle(); diff --git a/app/src/main/java/org/vostan/banvor/board/Animator.java b/app/src/main/java/org/vostan/banvor/board/Animator.java index f751e8a..8340be3 100644 --- a/app/src/main/java/org/vostan/banvor/board/Animator.java +++ b/app/src/main/java/org/vostan/banvor/board/Animator.java @@ -5,6 +5,9 @@ package org.vostan.banvor.board; import androidx.core.view.ViewCompat; + +import org.vostan.banvor.model.XYPair; + import java.util.LinkedList; /** @@ -28,21 +31,17 @@ public class Animator implements Runnable protected static abstract class XYAction extends Action { - int x; - int y; + protected XYPair _xy; - public XYAction( int _x, int _y ) - { - x = _x; - y = _y; + public XYAction( XYPair xy ){ + _xy = xy; } } public static class Move extends XYAction { - public Move( int x, int y ) - { - super(x,y); + public Move( XYPair xy ){ + super(xy); } public void intermediate( Animator ap, int step ) @@ -50,16 +49,15 @@ public class Animator implements Runnable public void last( Animator ap ) { - ap.view.getPuzzle().walk(x,y); + ap.view.getPuzzle().walk(_xy); ap.view.invalidate(); } } public static class Push extends XYAction { - public Push( int x, int y ) - { - super(x,y); + public Push( XYPair xy ){ + super(xy); } public void intermediate( Animator ap, int step ) @@ -68,16 +66,15 @@ public class Animator implements Runnable public void last( Animator ap ) { - ap.view.getPuzzle().push(x,y); + ap.view.getPuzzle().push(_xy); ap.view.invalidate(); } } public static class Select extends XYAction { - public Select(int x, int y ) - { - super(x,y); + public Select( XYPair xy ){ + super(xy); } public void intermediate( Animator ap, int step ) @@ -86,7 +83,7 @@ public class Animator implements Runnable public void last( Animator ap ) { - ap.view.getPuzzle().select(x,y); + ap.view.getPuzzle().select(_xy); ap.view.invalidate(); } } @@ -108,9 +105,8 @@ public class Animator implements Runnable public static class NoMove extends XYAction { - public NoMove( int x, int y ) - { - super(x,y); + public NoMove( XYPair xy ){ + super(xy); } public void intermediate( Animator ap, int step ) diff --git a/app/src/main/java/org/vostan/banvor/board/PuzzleControl.java b/app/src/main/java/org/vostan/banvor/board/PuzzleControl.java index 889c262..1d7c3db 100644 --- a/app/src/main/java/org/vostan/banvor/board/PuzzleControl.java +++ b/app/src/main/java/org/vostan/banvor/board/PuzzleControl.java @@ -13,6 +13,7 @@ import android.view.ScaleGestureDetector; import org.vostan.banvor.game.PuzzleLogic; import org.vostan.banvor.model.Puzzle; +import org.vostan.banvor.model.XYPair; //import android.support.v4.view.GestureDetectorCompat; @@ -117,7 +118,7 @@ public class PuzzleControl extends PuzzleView // If the tap is not on a valid tile then there is not point to // continue. // - if ( !puzzle.isValid(singleTapTile.x, singleTapTile.y) ) + if ( !puzzle.isValid(new XYPair(singleTapTile.x, singleTapTile.y) )) return true; // // Create sequence of steps and then animate it. diff --git a/app/src/main/java/org/vostan/banvor/board/PuzzleView.java b/app/src/main/java/org/vostan/banvor/board/PuzzleView.java index 5e8ba3e..aef66c9 100644 --- a/app/src/main/java/org/vostan/banvor/board/PuzzleView.java +++ b/app/src/main/java/org/vostan/banvor/board/PuzzleView.java @@ -18,6 +18,7 @@ import static java.lang.Math.*; import org.vostan.banvor.model.Puzzle; import static org.vostan.banvor.model.Puzzle.*; import org.vostan.banvor.R; +import org.vostan.banvor.model.XYPair; /** */ @@ -381,7 +382,7 @@ public class PuzzleView extends View //tile_hlpr.roundOut(tile_hlpr); //tile_hlpr.right -=1; //tile_hlpr.bottom -=1; - int sym = puzzle.getSym(x,y); + int sym = puzzle.getSym(new XYPair(x,y)); if ( !Puzzle.hasWorker(sym) ) { canvas.drawBitmap(tile[sym], @@ -403,30 +404,30 @@ public class PuzzleView extends View Bitmap normal_worker = ((sym&SELECTED)!=0) ? tile[WORKER|SELECTED] : tile[WORKER]; int direction = sym & WORKER_MASK; - if ( direction == WORKER_NORTH ) - { - canvas.translate(tile_dest.centerX(), tile_dest.centerY()); - canvas.rotate(180+rotated); - canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); - } - else if ( direction == WORKER_SOUTH ) + if ( direction == WORKER_FACE_DOWN) { canvas.translate(tile_dest.centerX(), tile_dest.centerY()); canvas.rotate(0+rotated); canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); } - else if ( direction == WORKER_WEST ) + else if ( direction == WORKER_FACE_UP) { canvas.translate(tile_dest.centerX(), tile_dest.centerY()); - canvas.rotate(90+rotated); + canvas.rotate(180+rotated); canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); } - else if ( direction == WORKER_EAST ) + else if ( direction == WORKER_FACE_LEFT) { canvas.translate(tile_dest.centerX(), tile_dest.centerY()); canvas.rotate(-90+rotated); canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); } + else if ( direction == WORKER_FACE_RIGHT) + { + canvas.translate(tile_dest.centerX(), tile_dest.centerY()); + canvas.rotate(90+rotated); + canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); + } canvas.drawBitmap(normal_worker, tile_src,tile_dest, mPaint); diff --git a/app/src/main/java/org/vostan/banvor/game/PuzzleLogic.java b/app/src/main/java/org/vostan/banvor/game/PuzzleLogic.java index 474d6dc..ad897ec 100644 --- a/app/src/main/java/org/vostan/banvor/game/PuzzleLogic.java +++ b/app/src/main/java/org/vostan/banvor/game/PuzzleLogic.java @@ -6,9 +6,12 @@ package org.vostan.banvor.game; import static java.lang.Math.*; import java.util.Arrays; +import java.util.ListIterator; +import java.util.Vector; import org.vostan.banvor.board.Animator; import org.vostan.banvor.model.Puzzle; +import org.vostan.banvor.model.XYPair; /** * @@ -18,7 +21,7 @@ public class PuzzleLogic //protected PlayActivity activity; protected Puzzle puzzle = null; protected int [] moves; - protected int [] setOfCells; + protected Vector setOfCells = new Vector(); public PuzzleLogic() {} @@ -27,21 +30,25 @@ public class PuzzleLogic { puzzle = p; moves = new int[puzzle.getColumnCount()*puzzle.getRowCount()]; - setOfCells = new int[2*puzzle.getColumnCount()*puzzle.getRowCount()]; + setOfCells.ensureCapacity(puzzle.getColumnCount()*puzzle.getRowCount()); } - + public boolean createSteps(Animator animator, final int x, final int y ) + { + return createSteps(animator, new XYPair(x,y)); + } + public boolean createSteps(Animator animator, XYPair xy ) { // // Check that the x,y are valid. // - if ( !puzzle.isValid(x, y) ) + if ( !puzzle.isValid(xy) ) return false; // // Now check what tile was tapped. // If the tapped is a floor then ... // - int tile = puzzle.getSym( x, y); + int tile = puzzle.getSym(xy); if ( Puzzle.isEmpty(tile) ) { // @@ -53,18 +60,18 @@ public class PuzzleLogic // If yes then we are done. // if ( puzzle.isSelected() - && tryMoveBox(animator, x, y) ) + && tryMoveBox(animator, xy) ) return true; // // Either nothing was selected or the box cannot be moved to // tapped location. Try move worker alone. // - if ( tryMoveWorker( animator, x, y ) ) + if ( tryMoveWorker( animator, xy ) ) return true; // // Show that action is not allowed. // - undoable( animator, x, y ); + undoable(animator, xy); } // // The tapped is the worker. Try move the box. If not possible @@ -85,7 +92,7 @@ public class PuzzleLogic // Check if worker selected a box and can push it to the location? // If yes then we are done. // - if ( tryMoveBox(animator, x, y) ) + if ( tryMoveBox(animator, xy) ) return true; // // If the box is not movable then unselect it. @@ -104,7 +111,7 @@ public class PuzzleLogic // // If the box is selected then unselect it. // - if ( puzzle.isSelected(x,y) ) + if ( puzzle.isSelected(xy) ) { unselect( animator ); return true; @@ -113,12 +120,12 @@ public class PuzzleLogic // Try move the worker next to the box and select it if the // box is not selected yet. // - if ( trySelectBox( animator, x, y ) ) + if ( trySelectBox( animator, xy ) ) return true; // // Show that action is not allowed if reached till here. // - undoable( animator, x, y ); + undoable( animator, xy ); } return true; } @@ -127,12 +134,12 @@ public class PuzzleLogic // // Routes to accessible cells from where worker stands. // - protected boolean tryMoveWorker( Animator animator, int x, int y ) + protected boolean tryMoveWorker( Animator animator, XYPair xy ) { // // If the filed is not accessable then move failed. // - if ( !isAccessible(x,y) ) + if ( !isAccessible(xy) ) return false; // // First unselect box. @@ -142,16 +149,18 @@ public class PuzzleLogic // // Get directions and queue moves accordingly. // - int dirs[] = getDirections(x, y); - for ( int i = 0; i < dirs.length; i+=2 ) - move( animator, dirs[i],dirs[i+1] ); + Vector dirs = getDirections(xy); + ListIterator it = dirs.listIterator(dirs.size()); + while ( it.hasPrevious() ) { + move(animator, it.previous()); + } // // Done. // return true; } - protected boolean tryMoveBox( Animator animator, int x, int y ) + protected boolean tryMoveBox( Animator animator, XYPair xy ) { // // If no box is selected then we cannot move no box. @@ -161,51 +170,44 @@ public class PuzzleLogic // // Check that asked move is orthogonal to the slected box. // - int bx = puzzle.getSelectedX(); - int by = puzzle.getSelectedY(); - int dx = x-bx; - int dy = y-by; - if ( dx != 0 && dy != 0 ) + XYPair box_xy = puzzle.getSelected(); + XYPair dxy = xy.sub(box_xy); + if ( dxy.x() != 0 && dxy.y() != 0 ) return false; // // There is no point to continue also in case if the asked cell // is the box. // - if ( dx == 0 && dy == 0 ) + if ( dxy.isEqual(XYPair.ZERO) ) return false; // // Now find the desired place for the worker to start push this // box. // - int wx = bx; - int wy = by; - if ( x < bx ) - ++wx; - else if ( x > bx ) - --wx; - else if ( y < by ) - ++wy; + XYPair w = new XYPair(box_xy); + if ( xy.x() < box_xy.x() ) + w = w.right(); + else if ( xy.x() > box_xy.x() ) + w = w.left(); + else if ( xy.y() < box_xy.y() ) + w = w.up(); else - --wy; + w = w.down(); // // Check if the desired place for the worker is accessable? If not // then there is no point to continue. // - if ( !isAccessible(wx, wy) ) + if ( !isAccessible(w) ) return false; // // Now check that all cell till x,y are empty and that we can // push box till there. // - int sx = bx-wx; - int sy = by-wy; - int ix = bx; - int iy = by; - while ( x != ix || y != iy ) + XYPair step_xy = box_xy.sub(w); + for ( XYPair i = box_xy; !xy.isEqual(i); ) { - ix+=sx; - iy+=sy; - int v = puzzle.getSym(ix,iy); + i = i.add(step_xy); + int v = puzzle.getSym(i); if ( !puzzle.isEmpty(v) && !puzzle.hasWorker(v) ) return false; } @@ -214,21 +216,16 @@ public class PuzzleLogic // what to do. First move worker to desired position if he is not // there already. // - if ( wx != puzzle.getWorkerX() || wy != puzzle.getWorkerY() ) + if ( !w.isEqual(puzzle.getWorker()) ) { - tryMoveWorker( animator, wx, wy ); - select( animator, bx, by ); + tryMoveWorker( animator, w ); + select( animator, box_xy ); } // // Now create the steps to push the box. // - ix = bx; - iy = by; - while ( x != ix || y != iy ) - { - push( animator, ix, iy ); - ix+=sx; - iy+=sy; + for ( XYPair i = box_xy; !i.isEqual(xy); i = i.add(step_xy) ) { + push( animator, i ); } // // Done @@ -236,12 +233,12 @@ public class PuzzleLogic return true; } - protected boolean trySelectBox( Animator animator, int x, int y ) + protected boolean trySelectBox( Animator animator, XYPair xy ) { - int north = puzzle.getSym(x,y-1); - int south = puzzle.getSym(x,y+1); - int west = puzzle.getSym(x-1,y); - int east = puzzle.getSym(x+1,y); + int north = puzzle.getSym(xy.up()); + int south = puzzle.getSym(xy.down()); + int west = puzzle.getSym(xy.left()); + int east = puzzle.getSym(xy.right()); // // First check if there is a worker in a nighbour cell. If // yes then simplly select the box. If the box is already selected @@ -253,11 +250,11 @@ public class PuzzleLogic || Puzzle.hasWorker( north ) || Puzzle.hasWorker( south ) ) { - if ( !puzzle.isSelected(x,y) ) + if ( !puzzle.isSelected(xy) ) { if ( puzzle.isSelected() ) unselect( animator ); - select( animator, x, y ); + select( animator, xy ); } } // @@ -266,48 +263,43 @@ public class PuzzleLogic // else { - int pref_x = -1; - int pref_y = -1; + XYPair pref = new XYPair(-1, -1); int shortest = Integer.MAX_VALUE; if ( Puzzle.isEmpty( north ) - && shortest > stepsAway( x, y-1) ) + && shortest > stepsAway(xy.down()) ) { - shortest = stepsAway( x, y-1); - pref_x = x; - pref_y = y-1; + shortest = stepsAway(xy.down()); + pref.set(xy.down()); } if ( Puzzle.isEmpty( south ) - && shortest > stepsAway( x, y+1) ) + && shortest > stepsAway(xy.up()) ) { - shortest = stepsAway( x, y+1); - pref_x = x; - pref_y = y+1; + shortest = stepsAway(xy.up()); + pref.set(xy.up()); } if ( Puzzle.isEmpty( west ) - && shortest > stepsAway( x-1, y) ) + && shortest > stepsAway(xy.left()) ) { - shortest = stepsAway( x-1, y); - pref_x = x-1; - pref_y = y; + shortest = stepsAway(xy.left()); + pref.set(xy.left()); } if ( Puzzle.isEmpty( east ) - && shortest > stepsAway( x+1, y) ) + && shortest > stepsAway( xy.right()) ) { - shortest = stepsAway( x+1, y); - pref_x = x+1; - pref_y = y; + shortest = stepsAway( xy.right()); + pref.set(xy.right()); } // // Move the worker to the direction. If we cannot move worker // next to the box then we cannot select it. // - if ( !puzzle.isValid(pref_x, pref_y) - || !tryMoveWorker(animator, pref_x, pref_y) ) + if ( !puzzle.isValid(pref) + || !tryMoveWorker(animator, pref) ) return false; // // Select the box. // - select(animator,x,y); + select(animator,xy); } // // Done @@ -315,19 +307,25 @@ public class PuzzleLogic return true; } - protected void move( Animator animator, int x, int y ) + + ////////////////////////////////////////////////////////////////////////// + // + // These should move to a callback. + // + + protected void move( Animator animator, XYPair xy ) { - animator.queue( new Animator.Move(x,y) ); + animator.queue( new Animator.Move(xy) ); } - protected void push( Animator animator, int x, int y ) + protected void push( Animator animator, XYPair xy ) { - animator.queue( new Animator.Push(x,y) ); + animator.queue( new Animator.Push(xy) ); } - protected void select( Animator animator, int x, int y ) + protected void select( Animator animator, XYPair xy ) { - animator.queue( new Animator.Select(x,y) ); + animator.queue( new Animator.Select(xy) ); } protected void unselect( Animator animator ) @@ -340,9 +338,9 @@ public class PuzzleLogic //animator.queue( new Animator.NoMove(x,y) ); } - protected void undoable( Animator animator, int x, int y ) + protected void undoable( Animator animator, XYPair xy ) { - animator.queue( new Animator.NoMove(x,y) ); + animator.queue( new Animator.NoMove(xy) ); } ////////////////////////////////////////////////////////////////////////// @@ -350,29 +348,28 @@ public class PuzzleLogic // Routes to accessible cells from where worker stands. // - protected final int getMoves( int x, int y ) + protected final int getMoves( XYPair xy ) { - return moves[puzzle.getIndex(x,y)]; + return moves[puzzle.getIndex(xy)]; } - protected void setMoves( int x, int y, int v ) + protected void setMoves( XYPair xy, int v ) { - moves[puzzle.getIndex(x,y)]=v; + moves[puzzle.getIndex(xy)]=v; } - private boolean setMovesIfGreater( int x, int y, int l ) + private boolean setMovesIfGreater( XYPair xy, int l ) { // // If out of borders then nothing to do. // - if ( y < 0 || y >= puzzle.getRowCount() - || x < 0 || x >= puzzle.getColumnCount() ) + if ( !puzzle.isValid(xy) ) return false; // // Check if the cell is a floor or goal. If yes then set the l // if current value is greater than l. // - if ( getMoves(x,y) > l && puzzle.isEmpty(puzzle.getSym(x,y))) + if ( getMoves(xy) > l && puzzle.isEmpty(puzzle.getSym(xy))) { - setMoves(x,y,l); + setMoves(xy,l); return true; } else @@ -389,105 +386,84 @@ public class PuzzleLogic // For the beginning there is no cell in the list. // int front = 0; - int last = 0; + setOfCells.removeAllElements(); // // Set the seed. // - setMoves(puzzle.getWorkerX(),puzzle.getWorkerY(),0); - setOfCells[last++] = puzzle.getWorkerX(); - setOfCells[last++] = puzzle.getWorkerY(); + setMoves(puzzle.getWorker(),0); + setOfCells.add(puzzle.getWorker()); // // Now on each loop pop one cell from the list and calculate the // distance of cell around that cell. // - while ( front < last ) + while ( front < setOfCells.size() ) { // // Pop the cell. // - int x = setOfCells[front++]; - int y = setOfCells[front++]; + XYPair xy = setOfCells.elementAt(front++); // // Increase the length of cells all around given cell and push // them into the list. // - int l = getMoves(x,y)+1; - if ( setMovesIfGreater(x-1,y,l) ) - { - setOfCells[last++] = x-1; - setOfCells[last++] = y; + int l = getMoves(xy)+1; + if ( setMovesIfGreater(xy.left(),l) ) { + setOfCells.add(xy.left()); } - if ( setMovesIfGreater(x+1,y,l) ) - { - setOfCells[last++] = x+1; - setOfCells[last++] = y; + if ( setMovesIfGreater(xy.right(),l) ) { + setOfCells.add(xy.right()); } - if ( setMovesIfGreater(x,y-1,l) ) - { - setOfCells[last++] = x; - setOfCells[last++] = y-1; + if ( setMovesIfGreater(xy.down(),l) ) { + setOfCells.add(xy.down()); } - if ( setMovesIfGreater(x,y+1,l) ) - { - setOfCells[last++] = x; - setOfCells[last++] = y+1; + if ( setMovesIfGreater(xy.up(),l) ) { + setOfCells.add(xy.up()); } } } - public final int stepsAway( int x, int y ) + public final int stepsAway( XYPair xy ) { - return getMoves(x,y); + return getMoves(xy); } - public final boolean isAccessible( int x, int y ) + public final boolean isAccessible( XYPair xy ) { - return puzzle.isValid(x, y) && stepsAway(x, y) != Integer.MAX_VALUE; + return puzzle.isValid(xy) && stepsAway(xy) != Integer.MAX_VALUE; } - public int [] getDirections( int x, int y ) + public Vector getDirections( XYPair xy ) { - int away = stepsAway(x,y); + int away = stepsAway(xy); if (away == Integer.MAX_VALUE) return null; // // Ok looks there is a routh to given cell. Now create an array // and fill in the step to get to the cell. // - int [] steps = new int[2*away]; - int i = steps.length; - steps[--i] = y; - steps[--i] = x; - while ( i > 0 ) + Vector steps = new Vector(away); + steps.add(xy); + while ( steps.size() < away ) { - x = steps[i]; - y = steps[i+1]; - int j = stepsAway(x,y); - if ( stepsAway(x-1,y) < j ) - { - steps[--i] = y; - steps[--i] = x-1; + xy = steps.lastElement(); + int j = stepsAway(xy); + if ( stepsAway(xy.left()) < j ) { + steps.add(xy.left()); } - else if ( stepsAway(x+1,y) < j ) - { - steps[--i] = y; - steps[--i] = x+1; + else if ( stepsAway(xy.right()) < j ) { + steps.add(xy.right()); } - else if ( stepsAway(x,y-1) < j ) - { - steps[--i] = y-1; - steps[--i] = x; + else if ( stepsAway(xy.down()) < j ) { + steps.add(xy.down()); } - else if ( stepsAway(x,y+1) < j ) - { - steps[--i] = y+1; - steps[--i] = x; + else if ( stepsAway(xy.up()) < j ) { + steps.add(xy.up()); } } return steps; } - public int [] getPushDirections( int x, int y ) + public XYPair [] getPushDirections( XYPair xy ) { // // The selected box can be moved only orthogonally. @@ -498,44 +474,36 @@ public class PuzzleLogic // push direction is directed to x,y and is not opposit. In other words // selected->x,y and worker->selected are codirectional. // - int selx = puzzle.getSelectedX(); - int sely = puzzle.getSelectedY(); - int xx = x-selx; - int yy = y-sely; - int sx = selx-puzzle.getWorkerX(); - int sy = sely-puzzle.getWorkerY(); - double scaliar = (double)(xx)*(double)(sx) - +(double)(yy)*(double)(sy); - int len = abs(xx)+abs(yy); + XYPair sel = puzzle.getSelected(); + XYPair dist = XYPair.sub(xy, puzzle.getSelected()); + XYPair dir = XYPair.sub(puzzle.getSelected(), puzzle.getWorker()); + double scaliar = (double)(dist.x())*(double)(dir.x()) + +(double)(dist.y())*(double)(dir.y()); + int len = dist.l1_norm(); if ( scaliar != (double)len ) return null; // // Now check that all cell till x,y are free. // - int ix = selx; - int iy = sely; - while ( x != ix || y != iy ) + XYPair ixy = new XYPair(puzzle.getSelected()); + while ( !xy.isEqual(ixy) ) { - ix+=sx; - iy+=sy; - if ( !puzzle.isEmpty(puzzle.getSym(ix,iy)) ) + ixy = ixy.add(dir); + if ( !puzzle.isEmpty(puzzle.getSym(ixy)) ) return null; } // // Looks we could move the box till x,y. Create the steps array // and fill it with push steps. // - int steps[] = new int [2*len]; + XYPair steps[] = new XYPair[len]; int i = 0; - ix = selx; - iy = sely; - while ( x != ix || y != iy ) + ixy.set(puzzle.getSelected()); + while ( !xy.isEqual(ixy) ) { - steps[i++] = ix; - steps[i++] = iy; - ix+=sx; - iy+=sy; - } + steps[i++] = ixy; + ixy = ixy.add(dir); + } return steps; } } diff --git a/app/src/main/java/org/vostan/banvor/model/Coord.java b/app/src/main/java/org/vostan/banvor/model/Coord.java deleted file mode 100644 index 4d37602..0000000 --- a/app/src/main/java/org/vostan/banvor/model/Coord.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.vostan.banvor.model; - -public class Coord { - public int x; - public int y; -} diff --git a/app/src/main/java/org/vostan/banvor/model/Puzzle.java b/app/src/main/java/org/vostan/banvor/model/Puzzle.java index a6df76a..ace2ba8 100644 --- a/app/src/main/java/org/vostan/banvor/model/Puzzle.java +++ b/app/src/main/java/org/vostan/banvor/model/Puzzle.java @@ -1,14 +1,9 @@ package org.vostan.banvor.model; //import java.lang.Exception; -import java.io.*; -import static java.lang.Math.*; -import android.util.Log; import java.util.LinkedList; -import static org.vostan.banvor.App.TAG; - public class Puzzle { public final static int FLOOR = 0x0; @@ -18,28 +13,45 @@ public class Puzzle public final static int BOX = 0x4; public final static int BINGO = BOX | GOAL; public final static int WORKER = 0x8; - public final static int WORKER_NORTH = 0x00 | WORKER; - public final static int WORKER_SOUTH = 0x10 | WORKER; - public final static int WORKER_WEST = 0x20 | WORKER; - public final static int WORKER_EAST = 0x30 | WORKER; - public final static int WORKER_MASK = WORKER_EAST | WORKER_WEST | WORKER_SOUTH | WORKER_NORTH; + public final static int WORKER_FACE_DOWN = 0x00 | WORKER; + public final static int WORKER_FACE_UP = 0x10 | WORKER; + public final static int WORKER_FACE_LEFT = 0x20 | WORKER; + public final static int WORKER_FACE_RIGHT = 0x30 | WORKER; + public final static int WORKER_MASK = WORKER_FACE_RIGHT | WORKER_FACE_LEFT | WORKER_FACE_UP | WORKER_FACE_DOWN; public final static int SELECTED = 0x40; public final static int MAX_COUNT = 0x80-1; - protected int columns; - protected int rows; - protected int box_count; + private final static XYPair unselected = new XYPair(-1,-1); + private final static XYPair zero = new XYPair(0,0); - protected int [] board; - protected int worker_x; - protected int worker_y; - protected int selected_x; - protected int selected_y; - protected int bingos; - public Puzzle( InputStream is ) + private int [] board; + private XYPair board_size = new XYPair(0,0); + private int bingos = 0; + private int box_count = 0; + private XYPair worker = new XYPair(0,0); + private XYPair selected = new XYPair(-1,-1); + + public Puzzle(){ + } + public Puzzle(int cols, int rows, int [] b) { - load( is ); + board_size.set(new XYPair(cols, rows)); + box_count = 0; + bingos = 0; +// board = new int[board_size.x()*board_size.y()]; + board = b; + for( int i = 0; i < board.length; ++i ){ + if ((board[i] & BOX) == BOX){ + ++box_count; + } + if ((board[i] & BINGO) == BINGO) { + ++bingos; + } + if ((board[i] & WORKER) == WORKER){ + worker = getXY(i); + } + } } ////////////////////////////////////////////////////////////////////////// @@ -49,39 +61,42 @@ public class Puzzle public final int getColumnCount() { - return columns; + return board_size.x(); } public final int getRowCount() { - return rows; + return board_size.y(); } public static int getSymCount() { return MAX_COUNT; } - - public final int getIndex( int x, int y ) + + public final int getIndex( XYPair c ) { - return y*columns+x; + return c.y()*board_size.x()+c.x(); + } + public final XYPair getXY( int idx ) + { + return new XYPair(getX(idx), getY(idx)); } public final int getX( int idx ) { - return idx % columns; + return idx % board_size.x(); } public final int getY( int idx ) { - return idx / columns; + return idx / board_size.x(); } - public final int getSym( int x, int y ) - { - return board[getIndex(x,y)]; + public final int getSym( XYPair c ) { + return board[getIndex(c)]; } - public void setSym( int x, int y, int v ) + public void setSym( XYPair xy, int v ) { - board[getIndex(x,y)]=v; + board[getIndex(xy)]=v; } - + public static boolean isEmpty( int v ) { return (v & FLOOR_MASK) == 0; @@ -95,47 +110,33 @@ public class Puzzle return (v & WORKER) != 0; } - public final int getWorkerX() + public final XYPair getWorker() { - return worker_x; - } - - public final int getWorkerY() - { - return worker_y; + return worker; } public final boolean isSelected() { - return selected_x != -1; + return !selected.isEqual(unselected); } - public final boolean isSelected( int x, int y ) + public final boolean isSelected( XYPair s ) { - return selected_x == x && selected_y == y; + return selected.isEqual(s); } - public final int getSelectedX() + public final XYPair getSelected() { - return selected_x; + return selected; } - public final int getSelectedY() + public final boolean isValid( XYPair xy ) { - return selected_y; + return xy.isInside(zero,board_size); } - public final boolean isValid( int x, int y ) - { - return ( 0 <= x && 0 <= y - && x < getColumnCount() - && y < getRowCount() ); - } - - public final boolean isOneStep( int x, int y ) - { - return isValid(x,y) - && (abs(worker_x-x )+abs(worker_y-y) == 1); + public final boolean isOneStep( XYPair xy ) { + return isValid(xy) && XYPair.sub(worker, xy).l1_norm() == 1; } public final boolean isDone() @@ -143,73 +144,42 @@ public class Puzzle return bingos == box_count; } - ////////////////////////////////////////////////////////////////////////// - // - // Loading. - // - private void load( InputStream is ) - {try{ - columns = is.read(); - rows = is.read(); - box_count = 0; - bingos = 0; - selected_x = -1; - selected_y = -1; - board = new int[columns*rows]; - byte [] b = new byte [columns*rows]; - is.read( b, 0, columns*rows); - for( int i = 0; i < b.length; ++i ) - { - switch( b[i] ) - { - case 0: - board[i] = FLOOR; - break; - case 1: - board[i] = WALL; - break; - case 2: - board[i] = BOX; - ++box_count; - break; - case 3: - board[i] = GOAL; - break; - case 4: - board[i] = BINGO; - ++box_count; - ++bingos; - break; - case 5: - board[i] = WORKER; - worker_x = getX(i); - worker_y = getY(i); - break; - default: - } - } - } - catch ( Exception e ) - { - Log.d( TAG, "load()", e); - }} - ////////////////////////////////////////////////////////////////////////// // // Move worker. // - - public boolean walk( int x, int y) + + private int worker_direction(XYPair xy) { + // + // Find direction to move. + // + int w; + if (worker.left().isEqual(xy)) { + w = WORKER_FACE_LEFT; + } + else if (worker.right().isEqual(xy)) { + w = WORKER_FACE_RIGHT; + } + else if (worker.up().isEqual(xy)) { + w = WORKER_FACE_UP; + } + else { + w = WORKER_FACE_DOWN; + } + return w; + } + + public boolean walk(XYPair xy) { // // Check that this is one step away. // - if ( !isOneStep(x,y) ) + if ( !isOneStep(xy) ) return false; // // Check that this is empty space. // - int v = getSym(x,y); + int v = getSym(xy); if ( !isEmpty(v) ) return false; // @@ -218,38 +188,25 @@ public class Puzzle if ( isSelected() ) unselect(); // - // Find direction to move. - // - int worker; - if ( worker_x < x ) - worker = WORKER_WEST; - else if ( worker_x > x ) - worker = WORKER_EAST; - else if ( worker_y > y ) - worker = WORKER_SOUTH; - else - worker = WORKER_NORTH; - // // Move worker marker from current position to asked position. // - setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~WORKER_MASK); - worker_x =x; - worker_y =y; - setSym(worker_x,worker_y,getSym(worker_x,worker_y)|worker); + setSym(worker,getSym(worker)&~WORKER_MASK); + worker.set(xy); + setSym(worker,getSym(worker)|worker_direction(xy)); return true; } - public boolean select( int x, int y) + public boolean select(XYPair xy) { // // Check that this is one step away. // - if ( !isOneStep(x,y) ) + if ( !isOneStep(xy) ) return false; // // Check that this is empty space. // - int v = getSym(x,y); + int v = getSym(xy); if ( !hasBox(v) ) return false; // @@ -258,24 +215,11 @@ public class Puzzle if ( isSelected() ) unselect(); // - // Find direction to move. - // - int worker; - if ( worker_x < x ) - worker = WORKER_WEST; - else if ( worker_x > x ) - worker = WORKER_EAST; - else if ( worker_y > y ) - worker = WORKER_SOUTH; - else - worker = WORKER_NORTH; - // // Move worker marker from current position to asked position. // - selected_x =x; - selected_y =y; - setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~WORKER_MASK|worker|SELECTED); - setSym(selected_x,selected_y,getSym(selected_x,selected_y)|SELECTED); + selected.set(xy); + setSym(worker,getSym(worker)&~WORKER_MASK|SELECTED|worker_direction(xy)); + setSym(selected,getSym(selected)|SELECTED); return true; } @@ -289,14 +233,13 @@ public class Puzzle // // Move worker marker from current position to asked position. // - setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~SELECTED); - setSym(selected_x,selected_y,getSym(selected_x,selected_y)&~SELECTED); - selected_x =-1; - selected_y =-1; + setSym(worker,getSym(worker)&~SELECTED); + setSym(selected,getSym(selected)&~SELECTED); + selected.set(unselected); return true; } - public boolean push(int x, int y) + public boolean push(XYPair xy) { // // If not selected then do nothing. @@ -306,30 +249,29 @@ public class Puzzle // // Check that we go to the selected direction. // - if ( selected_x != x && selected_y != y ) + if ( !selected.isEqual(xy) ) return false; // // Check that this is a box that we move. // - int v = getSym(x,y); + int v = getSym(xy); if ( !hasBox(v) ) return false; // // Check that the next space to the box is empty. //w - 2*(w-x) = 2x -w - int next_x = 2*x - worker_x; - int next_y = 2*y - worker_y; - int next_v = getSym(next_x,next_y); + XYPair next = xy.mul(2).sub(worker); + int next_v = getSym(next); if ( !isEmpty(next_v) ) return false; // // Ok, looks we can move the box. Do it actually. // unselect(); - setSym(x,y,getSym(x,y)&~BOX); - setSym(next_x,next_y,getSym(next_x,next_y)|BOX); - walk(x,y); - select(next_x,next_y); + setSym(xy,getSym(xy)&~BOX); + setSym(next,getSym(next)|BOX); + walk(xy); + select(next); // // Keep track of box count in place. // @@ -348,29 +290,23 @@ public class Puzzle protected class State { public int [] board; - public int worker_x; - public int worker_y; - public int selected_x; - public int selected_y; - public int bingos; + public XYPair worker = new XYPair(0,0); + public XYPair selected = new XYPair(0, 0); + public int bingos = 0; public State(Puzzle puzzle) { board = puzzle.board.clone(); - worker_x = puzzle.worker_x; - worker_y = puzzle.worker_y; - selected_x = puzzle.selected_x; - selected_y = puzzle.selected_y; + worker.set(puzzle.worker); + selected.set(puzzle.selected); bingos = puzzle.bingos; } public void restore(Puzzle puzzle) { puzzle.board = board; - puzzle.worker_x = worker_x; - puzzle.worker_y = worker_y; - puzzle.selected_x = selected_x; - puzzle.selected_y = selected_y; + puzzle.worker.set(worker); + puzzle.selected.set(selected); puzzle.bingos = bingos; } } diff --git a/app/src/main/java/org/vostan/banvor/model/PuzzleBinLoader.java b/app/src/main/java/org/vostan/banvor/model/PuzzleBinLoader.java new file mode 100644 index 0000000..e37f721 --- /dev/null +++ b/app/src/main/java/org/vostan/banvor/model/PuzzleBinLoader.java @@ -0,0 +1,111 @@ +package org.vostan.banvor.model; + +import static org.vostan.banvor.App.TAG; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; + +public class PuzzleBinLoader { + + /* + * Load puzzle from the puzzle.bin. + */ + public static Puzzle loadNthPuzzle( InputStream is, int i ) + {try{ + // + // Read amount of puzzles. + // + int count = read_i32(is); + if ( i >= count ) + return null; + // + // Read the offset. + // + is.skip( i*4 ); + int offset = read_i32(is); + // + // Jump to the offset and read the puzzle. + // + is.skip( offset - 4 - (i+1)*4); + Puzzle p = loadPuzzle(is); + // + // Finally return the puzzle and we are done. + // + return p; + } + catch ( java.lang.Exception e ) + { + //Log.d(TAG, "Exception: " + e.getMessage() ); + e.printStackTrace(); + return null; + }} + // + // Retrive amount of puzzles. + // + public static int getCount(InputStream is) + {try{ + int count = read_i32(is); + return count; + } + catch ( java.lang.Exception e ) + { + //Log.d(TAG, "Exception: " + e.getMessage() ); + e.printStackTrace(); + return 0; + }} + + private static Puzzle loadPuzzle( InputStream is ) + { try{ + int cols = is.read(); + int rows = is.read(); + + int [] board = new int[cols*rows]; + byte [] b = new byte [cols*rows]; + is.read( b, 0, cols*rows); + for( int i = 0; i < b.length; ++i ) + { + switch( b[i] ) + { + case 0: + board[i] = Puzzle.FLOOR; + break; + case 1: + board[i] = Puzzle.WALL; + break; + case 2: + board[i] = Puzzle.BOX; + break; + case 3: + board[i] = Puzzle.GOAL; + break; + case 4: + board[i] = Puzzle.BINGO; + break; + case 5: + board[i] = Puzzle.WORKER; + break; + default: + } + } + return new Puzzle(cols, rows, board); + } + catch ( Exception e ) + { + Log.d( TAG, "load()", e); + return null; + }} + + // + // Helper function to read little endian 32bit integer. + // + private static int read_i32( InputStream is ) throws IOException + { + int i = is.read(); + i |= is.read() << 8; + i |= is.read() << 16; + i |= is.read() << 24; + return i; + } +} diff --git a/app/src/main/java/org/vostan/banvor/model/PuzzleContainer.java b/app/src/main/java/org/vostan/banvor/model/PuzzleContainer.java index efe8e92..9ef24f9 100644 --- a/app/src/main/java/org/vostan/banvor/model/PuzzleContainer.java +++ b/app/src/main/java/org/vostan/banvor/model/PuzzleContainer.java @@ -15,63 +15,20 @@ public class PuzzleContainer implements IPuzzleSource * Load puzzle from the puzzle.bin. */ public Puzzle getPuzzle( int i ) - {try{ - InputStream is = theApp().getResources().openRawResource(R.raw.puzzles); - - // - // Read amount of puzzles. - // - count = read_i32(is); - if ( i >= count ) - return null; - // - // Read the offset. - // - is.skip( i*4 ); - int offset = read_i32(is); - // - // Jump to the offset and read the puzzle. - // - is.skip( offset - 4 - (i+1)*4); - Puzzle p = new Puzzle( is ); - // - // Finally return the puzzle and we are done. - // - return p; - } - catch ( java.lang.Exception e ) { - //Log.d(TAG, "Exception: " + e.getMessage() ); - e.printStackTrace(); - return null; - }} + InputStream is = theApp().getResources().openRawResource(R.raw.puzzles); + return PuzzleBinLoader.loadNthPuzzle(is, i); + } // - // Retrive amount of puzzles. + // Retrieve amount of puzzles. // public int getCount() - {try{ + { if ( count == 0 ) { InputStream is = theApp().getResources().openRawResource(R.raw.puzzles); - count = read_i32(is); + count = PuzzleBinLoader.getCount(is); } return count; } - catch ( java.lang.Exception e ) - { - //Log.d(TAG, "Exception: " + e.getMessage() ); - e.printStackTrace(); - return 0; - }} - // - // Helper function to read little endian 32bit integer. - // - protected int read_i32( InputStream is ) throws IOException - { - int i = is.read(); - i |= is.read() << 8; - i |= is.read() << 16; - i |= is.read() << 24; - return i; - } } diff --git a/app/src/main/java/org/vostan/banvor/model/XYPair.java b/app/src/main/java/org/vostan/banvor/model/XYPair.java new file mode 100644 index 0000000..5234d2d --- /dev/null +++ b/app/src/main/java/org/vostan/banvor/model/XYPair.java @@ -0,0 +1,89 @@ +package org.vostan.banvor.model; + +import static java.lang.Math.abs; + +public class XYPair { + public static final XYPair UP = new XYPair(0, 1); + public static final XYPair DOWN = new XYPair(0, -1); + public static final XYPair LEFT = new XYPair(-1, 0); + public static final XYPair RIGHT = new XYPair(1, 0); + public static final XYPair ZERO = new XYPair(0, 0); + private int _x = 0; + private int _y = 0; + + public XYPair(int x, int y){ + _x = x; + _y = y; + } + + public XYPair(XYPair op){ + _x = op.x(); + _y = op.y(); + } + + public int x(){ + return _x; + } + + public int y(){ + return _y; + } + + public void set(XYPair xy){ + _x = xy.x(); + _y = xy.y(); + } + + public static XYPair add(XYPair op1, XYPair op2){ + return new XYPair(op1._x+op2._x, op1._y+op2._y); + } + + public static XYPair sub(XYPair op1, XYPair op2){ + return new XYPair(op1._x-op2._x, op1._y-op2._y); + } + + public static XYPair mul(XYPair op1, int scaliar){ + return new XYPair(op1._x*scaliar, op1._y*scaliar); + } + + public XYPair mul(int scaliar){ + return mul(this, scaliar); + } + + public XYPair sub(XYPair op){ + return sub(this, op); + } + + public XYPair add(XYPair op){ + return add(this, op); + } + + public int l1_norm(){ + return abs(_x)+abs(_y); + } + + public boolean isEqual(XYPair op){ + return _x == op._x && _y == op._y; + } + + public boolean isInside(XYPair op1, XYPair op2){ + return op1.x() <= this.x() && this.x() < op2.x() + && op1.y() <= this.y() && this.y() < op2.y(); + } + + public XYPair left(){ + return this.add(LEFT); + } + + public XYPair right(){ + return this.add(RIGHT); + } + + public XYPair up(){ + return this.add(UP); + } + + public XYPair down(){ + return this.add(DOWN); + } +} diff --git a/app/src/test/java/org/vostan/banvor/ExampleUnitTest.java b/app/src/test/java/org/vostan/banvor/ExampleUnitTest.java deleted file mode 100644 index f79ad98..0000000 --- a/app/src/test/java/org/vostan/banvor/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.vostan.banvor; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/app/src/test/java/org/vostan/banvor/UnitTestXYPair.java b/app/src/test/java/org/vostan/banvor/UnitTestXYPair.java new file mode 100644 index 0000000..c88ab9e --- /dev/null +++ b/app/src/test/java/org/vostan/banvor/UnitTestXYPair.java @@ -0,0 +1,114 @@ +package org.vostan.banvor; + +import org.junit.Test; +import org.vostan.banvor.model.XYPair; + +import static org.junit.Assert.*; + +public class UnitTestXYPair { + public XYPair bottom_left = new XYPair(0, 0); + public XYPair upper_right = new XYPair(10, 10); + public XYPair middle = new XYPair(5, 5); + + @Test + public void isInside_bottom_left_is_inside() { + assertTrue(bottom_left.isInside(bottom_left, upper_right)); + assertTrue(new XYPair(bottom_left.x(), middle.y()).isInside(bottom_left, upper_right)); + assertTrue(new XYPair(middle.x(), bottom_left.y()).isInside(bottom_left, upper_right)); + } + + @Test + public void isInside_upper_right_is_not_inside() { + assertFalse(upper_right.isInside(bottom_left, upper_right)); + assertFalse(new XYPair(upper_right.x(), middle.y()).isInside(bottom_left, upper_right)); + assertFalse(new XYPair(middle.x(), upper_right.y()).isInside(bottom_left, upper_right)); + } + + @Test + public void isInside_middle_is_inside() { + assertTrue(middle.isInside(bottom_left, upper_right)); + } + + @Test + public void isInside_outside_is_not_inside() { + assertFalse(bottom_left.left().isInside(bottom_left, upper_right)); + assertFalse(new XYPair(middle.x(), bottom_left.y()-1).isInside(bottom_left, upper_right)); + assertFalse(new XYPair(upper_right.x()+1, middle.y()).isInside(bottom_left, upper_right)); + assertFalse(new XYPair(middle.x(), upper_right.y()+1).isInside(bottom_left, upper_right)); + } + + @Test + public void l1_norne_test() { + assertEquals(6, new XYPair(2,4).l1_norm()); + assertEquals(6, new XYPair(-2,4).l1_norm()); + assertEquals(6, new XYPair(2,-4).l1_norm()); + assertEquals(6, new XYPair(-2,-4).l1_norm()); + assertEquals(0, new XYPair(0,0).l1_norm()); + } + + @Test + public void add_test() { + XYPair c = XYPair.add(new XYPair(2,4), new XYPair(3, 5)); + assertEquals(5, c.x()); + assertEquals(9, c.y()); + + c = XYPair.add(new XYPair(-2,4), new XYPair(3, -5)); + assertEquals(1, c.x()); + assertEquals(-1, c.y()); + } + + @Test + public void sub_test() { + XYPair c = XYPair.sub(new XYPair(2,9), new XYPair(3, 5)); + assertEquals(-1, c.x()); + assertEquals(4, c.y()); + + c = XYPair.sub(new XYPair(-2,-4), new XYPair(3, -5)); + assertEquals(-5, c.x()); + assertEquals(1, c.y()); + } + + @Test + public void mul_test() { + XYPair c = XYPair.mul(new XYPair(2,9), 0); + assertEquals(0, c.x()); + assertEquals(0, c.y()); + + c = XYPair.mul(new XYPair(-2,-4), 3); + assertEquals(-6, c.x()); + assertEquals(-12, c.y()); + + c = XYPair.mul(new XYPair(-2,-4), -2); + assertEquals(4, c.x()); + assertEquals(8, c.y()); + } + + @Test + public void left_test() { + XYPair c = new XYPair(5,5).left(); + assertEquals(4, c.x()); + assertEquals(5, c.y()); + } + + @Test + public void right_test() { + XYPair c = new XYPair(5,5).right(); + assertEquals(6, c.x()); + assertEquals(5, c.y()); + } + + @Test + public void up_test() { + XYPair c = new XYPair(5,5).up(); + assertEquals(5, c.x()); + assertEquals(6, c.y()); + } + + @Test + public void down_test() { + XYPair c = new XYPair(5,5).down(); + assertEquals(5, c.x()); + assertEquals(4, c.y()); + } + +} diff --git a/build.gradle b/build.gradle index fa1f1dc..b68a347 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,10 @@ buildscript { repositories { google() mavenCentral() +//This is needed for SVG->PNG +// maven { +// url "https://plugins.gradle.org/m2/" +// } } dependencies { classpath "com.android.tools.build:gradle:7.0.2" @@ -10,6 +14,9 @@ buildscript { def nav_version = "2.3.5" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" +//This is needed for SVG->PNG +// classpath 'com.trello:victor:1.1.2' + // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files }