Refactoring to use XYPair + moving loader to a separate file.

This commit is contained in:
2021-10-17 17:11:58 +01:00
parent 0c0938d651
commit 56679bd1be
14 changed files with 642 additions and 473 deletions

View File

@@ -4,6 +4,9 @@ plugins {
apply plugin: "androidx.navigation.safeargs" 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. // Convert SVG images from art directory into PNG files.
// //
@@ -19,7 +22,7 @@ apply plugin: "androidx.navigation.safeargs"
//} //}
//preBuild.dependsOn generateDrawablesFromArt //preBuild.dependsOn generateDrawablesFromArt
String gitVersion() { static String gitVersion() {
def versionP = 'git describe --tags --long --dirty=-x --always --abbrev=8' def versionP = 'git describe --tags --long --dirty=-x --always --abbrev=8'
.execute() .execute()
versionP.waitFor() versionP.waitFor()
@@ -28,15 +31,15 @@ String gitVersion() {
return versionP.text.trim() return versionP.text.trim()
} }
String gitHash() { static String gitHash() {
def githashP = 'git rev-parse HEAD'.execute() def hashP = 'git rev-parse HEAD'.execute()
githashP.waitFor() hashP.waitFor()
if (githashP.exitValue()) if (hashP.exitValue())
throw new GradleException("Couldn't extract git_hash. Git exited unexpectedly.") 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() def gitP = 'git rev-list v2..HEAD --count'.execute()
gitP.waitFor() gitP.waitFor()
if (gitP.exitValue()) if (gitP.exitValue())
@@ -44,19 +47,26 @@ Integer gitVersionCode() {
return gitP.text.trim().toInteger() return gitP.text.trim().toInteger()
} }
String buildDate() { static String buildDate() {
def builddate = new Date() def date = new Date()
return builddate.toString() return date.toString()
} }
android { android {
signingConfigs { signingConfigs {
release { release {
storeFile file('/Users/vahagnk/devel/banvor/keys/signing_key') storeFile file('keys/signing_key')
keyAlias 'key0'
} }
} }
compileSdk 31 compileSdk 31
// sourceSets {
// main {
// svg.srcDir 'src/main/art'
// }
// }
defaultConfig { defaultConfig {
applicationId "org.vostan.banvor" applicationId "org.vostan.banvor"
minSdk 21 minSdk 21
@@ -106,6 +116,8 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0' implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
// This is for unit tests.
testImplementation 'junit:junit:4.+' testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

View File

@@ -61,23 +61,23 @@ public class PuzzleBoardFragment extends Fragment
public void onTouch() { public void onTouch() {
} }
}); });
binding.btnTest.setOnClickListener((View.OnClickListener) view4 -> { binding.btnTest.setOnClickListener((View.OnClickListener) v -> {
createNextLevelDialog((d, w) -> { createNextLevelDialog((d, w) -> {
initAndShowCurrentPuzzle(); initAndShowCurrentPuzzle();
}).show(); }).show();
}); });
binding.btnPrev.setOnClickListener((View.OnClickListener) view1 -> { binding.btnPrev.setOnClickListener((View.OnClickListener) v -> {
gameState.setCurrentLevel(gameState.getCurrentLevel()-1); gameState.setCurrentLevel(gameState.getCurrentLevel()-1);
PuzzleBoardFragment.this.initAndShowCurrentPuzzle(); PuzzleBoardFragment.this.initAndShowCurrentPuzzle();
}); });
binding.btnNext.setOnClickListener((View.OnClickListener) view2 -> { binding.btnNext.setOnClickListener((View.OnClickListener) v -> {
gameState.setCurrentLevel(gameState.getCurrentLevel()+1); gameState.setCurrentLevel(gameState.getCurrentLevel()+1);
PuzzleBoardFragment.this.initAndShowCurrentPuzzle(); PuzzleBoardFragment.this.initAndShowCurrentPuzzle();
}); });
binding.btnReset.setOnClickListener((View.OnClickListener) view3 -> { binding.btnReset.setOnClickListener((View.OnClickListener) v -> {
PuzzleBoardFragment.this.initAndShowCurrentPuzzle(); PuzzleBoardFragment.this.initAndShowCurrentPuzzle();
}); });
binding.btnUndo.setOnClickListener((View.OnClickListener) view4 -> { binding.btnUndo.setOnClickListener((View.OnClickListener) v -> {
createNextLevelDialog((d, w) -> { createNextLevelDialog((d, w) -> {
gameState.advanceCurrentLevel(); gameState.advanceCurrentLevel();
initAndShowCurrentPuzzle(); initAndShowCurrentPuzzle();

View File

@@ -5,6 +5,9 @@
package org.vostan.banvor.board; package org.vostan.banvor.board;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import org.vostan.banvor.model.XYPair;
import java.util.LinkedList; import java.util.LinkedList;
/** /**
@@ -28,21 +31,17 @@ public class Animator implements Runnable
protected static abstract class XYAction extends Action protected static abstract class XYAction extends Action
{ {
int x; protected XYPair _xy;
int y;
public XYAction( int _x, int _y ) public XYAction( XYPair xy ){
{ _xy = xy;
x = _x;
y = _y;
} }
} }
public static class Move extends XYAction public static class Move extends XYAction
{ {
public Move( int x, int y ) public Move( XYPair xy ){
{ super(xy);
super(x,y);
} }
public void intermediate( Animator ap, int step ) public void intermediate( Animator ap, int step )
@@ -50,16 +49,15 @@ public class Animator implements Runnable
public void last( Animator ap ) public void last( Animator ap )
{ {
ap.view.getPuzzle().walk(x,y); ap.view.getPuzzle().walk(_xy);
ap.view.invalidate(); ap.view.invalidate();
} }
} }
public static class Push extends XYAction public static class Push extends XYAction
{ {
public Push( int x, int y ) public Push( XYPair xy ){
{ super(xy);
super(x,y);
} }
public void intermediate( Animator ap, int step ) public void intermediate( Animator ap, int step )
@@ -68,16 +66,15 @@ public class Animator implements Runnable
public void last( Animator ap ) public void last( Animator ap )
{ {
ap.view.getPuzzle().push(x,y); ap.view.getPuzzle().push(_xy);
ap.view.invalidate(); ap.view.invalidate();
} }
} }
public static class Select extends XYAction public static class Select extends XYAction
{ {
public Select(int x, int y ) public Select( XYPair xy ){
{ super(xy);
super(x,y);
} }
public void intermediate( Animator ap, int step ) public void intermediate( Animator ap, int step )
@@ -86,7 +83,7 @@ public class Animator implements Runnable
public void last( Animator ap ) public void last( Animator ap )
{ {
ap.view.getPuzzle().select(x,y); ap.view.getPuzzle().select(_xy);
ap.view.invalidate(); ap.view.invalidate();
} }
} }
@@ -108,9 +105,8 @@ public class Animator implements Runnable
public static class NoMove extends XYAction public static class NoMove extends XYAction
{ {
public NoMove( int x, int y ) public NoMove( XYPair xy ){
{ super(xy);
super(x,y);
} }
public void intermediate( Animator ap, int step ) public void intermediate( Animator ap, int step )

View File

@@ -13,6 +13,7 @@ import android.view.ScaleGestureDetector;
import org.vostan.banvor.game.PuzzleLogic; import org.vostan.banvor.game.PuzzleLogic;
import org.vostan.banvor.model.Puzzle; import org.vostan.banvor.model.Puzzle;
import org.vostan.banvor.model.XYPair;
//import android.support.v4.view.GestureDetectorCompat; //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 // If the tap is not on a valid tile then there is not point to
// continue. // continue.
// //
if ( !puzzle.isValid(singleTapTile.x, singleTapTile.y) ) if ( !puzzle.isValid(new XYPair(singleTapTile.x, singleTapTile.y) ))
return true; return true;
// //
// Create sequence of steps and then animate it. // Create sequence of steps and then animate it.

View File

@@ -18,6 +18,7 @@ import static java.lang.Math.*;
import org.vostan.banvor.model.Puzzle; import org.vostan.banvor.model.Puzzle;
import static org.vostan.banvor.model.Puzzle.*; import static org.vostan.banvor.model.Puzzle.*;
import org.vostan.banvor.R; 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.roundOut(tile_hlpr);
//tile_hlpr.right -=1; //tile_hlpr.right -=1;
//tile_hlpr.bottom -=1; //tile_hlpr.bottom -=1;
int sym = puzzle.getSym(x,y); int sym = puzzle.getSym(new XYPair(x,y));
if ( !Puzzle.hasWorker(sym) ) if ( !Puzzle.hasWorker(sym) )
{ {
canvas.drawBitmap(tile[sym], canvas.drawBitmap(tile[sym],
@@ -403,30 +404,30 @@ public class PuzzleView extends View
Bitmap normal_worker = ((sym&SELECTED)!=0) ? Bitmap normal_worker = ((sym&SELECTED)!=0) ?
tile[WORKER|SELECTED] : tile[WORKER]; tile[WORKER|SELECTED] : tile[WORKER];
int direction = sym & WORKER_MASK; int direction = sym & WORKER_MASK;
if ( direction == WORKER_NORTH ) if ( direction == WORKER_FACE_DOWN)
{
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 )
{ {
canvas.translate(tile_dest.centerX(), tile_dest.centerY()); canvas.translate(tile_dest.centerX(), tile_dest.centerY());
canvas.rotate(0+rotated); canvas.rotate(0+rotated);
canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); 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.translate(tile_dest.centerX(), tile_dest.centerY());
canvas.rotate(90+rotated); canvas.rotate(180+rotated);
canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); 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.translate(tile_dest.centerX(), tile_dest.centerY());
canvas.rotate(-90+rotated); canvas.rotate(-90+rotated);
canvas.translate(-tile_dest.centerX(), -tile_dest.centerY()); 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, canvas.drawBitmap(normal_worker,
tile_src,tile_dest, tile_src,tile_dest,
mPaint); mPaint);

View File

@@ -6,9 +6,12 @@ package org.vostan.banvor.game;
import static java.lang.Math.*; import static java.lang.Math.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.ListIterator;
import java.util.Vector;
import org.vostan.banvor.board.Animator; import org.vostan.banvor.board.Animator;
import org.vostan.banvor.model.Puzzle; import org.vostan.banvor.model.Puzzle;
import org.vostan.banvor.model.XYPair;
/** /**
* *
@@ -18,7 +21,7 @@ public class PuzzleLogic
//protected PlayActivity activity; //protected PlayActivity activity;
protected Puzzle puzzle = null; protected Puzzle puzzle = null;
protected int [] moves; protected int [] moves;
protected int [] setOfCells; protected Vector<XYPair> setOfCells = new Vector<XYPair>();
public PuzzleLogic() public PuzzleLogic()
{} {}
@@ -27,21 +30,25 @@ public class PuzzleLogic
{ {
puzzle = p; puzzle = p;
moves = new int[puzzle.getColumnCount()*puzzle.getRowCount()]; 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 ) 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. // Check that the x,y are valid.
// //
if ( !puzzle.isValid(x, y) ) if ( !puzzle.isValid(xy) )
return false; return false;
// //
// Now check what tile was tapped. // Now check what tile was tapped.
// If the tapped is a floor then ... // If the tapped is a floor then ...
// //
int tile = puzzle.getSym( x, y); int tile = puzzle.getSym(xy);
if ( Puzzle.isEmpty(tile) ) if ( Puzzle.isEmpty(tile) )
{ {
// //
@@ -53,18 +60,18 @@ public class PuzzleLogic
// If yes then we are done. // If yes then we are done.
// //
if ( puzzle.isSelected() if ( puzzle.isSelected()
&& tryMoveBox(animator, x, y) ) && tryMoveBox(animator, xy) )
return true; return true;
// //
// Either nothing was selected or the box cannot be moved to // Either nothing was selected or the box cannot be moved to
// tapped location. Try move worker alone. // tapped location. Try move worker alone.
// //
if ( tryMoveWorker( animator, x, y ) ) if ( tryMoveWorker( animator, xy ) )
return true; return true;
// //
// Show that action is not allowed. // 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 // 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? // Check if worker selected a box and can push it to the location?
// If yes then we are done. // If yes then we are done.
// //
if ( tryMoveBox(animator, x, y) ) if ( tryMoveBox(animator, xy) )
return true; return true;
// //
// If the box is not movable then unselect it. // 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 the box is selected then unselect it.
// //
if ( puzzle.isSelected(x,y) ) if ( puzzle.isSelected(xy) )
{ {
unselect( animator ); unselect( animator );
return true; return true;
@@ -113,12 +120,12 @@ public class PuzzleLogic
// Try move the worker next to the box and select it if the // Try move the worker next to the box and select it if the
// box is not selected yet. // box is not selected yet.
// //
if ( trySelectBox( animator, x, y ) ) if ( trySelectBox( animator, xy ) )
return true; return true;
// //
// Show that action is not allowed if reached till here. // Show that action is not allowed if reached till here.
// //
undoable( animator, x, y ); undoable( animator, xy );
} }
return true; return true;
} }
@@ -127,12 +134,12 @@ public class PuzzleLogic
// //
// Routes to accessible cells from where worker stands. // 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 the filed is not accessable then move failed.
// //
if ( !isAccessible(x,y) ) if ( !isAccessible(xy) )
return false; return false;
// //
// First unselect box. // First unselect box.
@@ -142,16 +149,18 @@ public class PuzzleLogic
// //
// Get directions and queue moves accordingly. // Get directions and queue moves accordingly.
// //
int dirs[] = getDirections(x, y); Vector<XYPair> dirs = getDirections(xy);
for ( int i = 0; i < dirs.length; i+=2 ) ListIterator<XYPair> it = dirs.listIterator(dirs.size());
move( animator, dirs[i],dirs[i+1] ); while ( it.hasPrevious() ) {
move(animator, it.previous());
}
// //
// Done. // Done.
// //
return true; 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. // 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. // Check that asked move is orthogonal to the slected box.
// //
int bx = puzzle.getSelectedX(); XYPair box_xy = puzzle.getSelected();
int by = puzzle.getSelectedY(); XYPair dxy = xy.sub(box_xy);
int dx = x-bx; if ( dxy.x() != 0 && dxy.y() != 0 )
int dy = y-by;
if ( dx != 0 && dy != 0 )
return false; return false;
// //
// There is no point to continue also in case if the asked cell // There is no point to continue also in case if the asked cell
// is the box. // is the box.
// //
if ( dx == 0 && dy == 0 ) if ( dxy.isEqual(XYPair.ZERO) )
return false; return false;
// //
// Now find the desired place for the worker to start push this // Now find the desired place for the worker to start push this
// box. // box.
// //
int wx = bx; XYPair w = new XYPair(box_xy);
int wy = by; if ( xy.x() < box_xy.x() )
if ( x < bx ) w = w.right();
++wx; else if ( xy.x() > box_xy.x() )
else if ( x > bx ) w = w.left();
--wx; else if ( xy.y() < box_xy.y() )
else if ( y < by ) w = w.up();
++wy;
else else
--wy; w = w.down();
// //
// Check if the desired place for the worker is accessable? If not // Check if the desired place for the worker is accessable? If not
// then there is no point to continue. // then there is no point to continue.
// //
if ( !isAccessible(wx, wy) ) if ( !isAccessible(w) )
return false; return false;
// //
// Now check that all cell till x,y are empty and that we can // Now check that all cell till x,y are empty and that we can
// push box till there. // push box till there.
// //
int sx = bx-wx; XYPair step_xy = box_xy.sub(w);
int sy = by-wy; for ( XYPair i = box_xy; !xy.isEqual(i); )
int ix = bx;
int iy = by;
while ( x != ix || y != iy )
{ {
ix+=sx; i = i.add(step_xy);
iy+=sy; int v = puzzle.getSym(i);
int v = puzzle.getSym(ix,iy);
if ( !puzzle.isEmpty(v) && !puzzle.hasWorker(v) ) if ( !puzzle.isEmpty(v) && !puzzle.hasWorker(v) )
return false; return false;
} }
@@ -214,21 +216,16 @@ public class PuzzleLogic
// what to do. First move worker to desired position if he is not // what to do. First move worker to desired position if he is not
// there already. // there already.
// //
if ( wx != puzzle.getWorkerX() || wy != puzzle.getWorkerY() ) if ( !w.isEqual(puzzle.getWorker()) )
{ {
tryMoveWorker( animator, wx, wy ); tryMoveWorker( animator, w );
select( animator, bx, by ); select( animator, box_xy );
} }
// //
// Now create the steps to push the box. // Now create the steps to push the box.
// //
ix = bx; for ( XYPair i = box_xy; !i.isEqual(xy); i = i.add(step_xy) ) {
iy = by; push( animator, i );
while ( x != ix || y != iy )
{
push( animator, ix, iy );
ix+=sx;
iy+=sy;
} }
// //
// Done // Done
@@ -236,12 +233,12 @@ public class PuzzleLogic
return true; 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 north = puzzle.getSym(xy.up());
int south = puzzle.getSym(x,y+1); int south = puzzle.getSym(xy.down());
int west = puzzle.getSym(x-1,y); int west = puzzle.getSym(xy.left());
int east = puzzle.getSym(x+1,y); int east = puzzle.getSym(xy.right());
// //
// First check if there is a worker in a nighbour cell. If // First check if there is a worker in a nighbour cell. If
// yes then simplly select the box. If the box is already selected // yes then simplly select the box. If the box is already selected
@@ -253,11 +250,11 @@ public class PuzzleLogic
|| Puzzle.hasWorker( north ) || Puzzle.hasWorker( north )
|| Puzzle.hasWorker( south ) ) || Puzzle.hasWorker( south ) )
{ {
if ( !puzzle.isSelected(x,y) ) if ( !puzzle.isSelected(xy) )
{ {
if ( puzzle.isSelected() ) if ( puzzle.isSelected() )
unselect( animator ); unselect( animator );
select( animator, x, y ); select( animator, xy );
} }
} }
// //
@@ -266,48 +263,43 @@ public class PuzzleLogic
// //
else else
{ {
int pref_x = -1; XYPair pref = new XYPair(-1, -1);
int pref_y = -1;
int shortest = Integer.MAX_VALUE; int shortest = Integer.MAX_VALUE;
if ( Puzzle.isEmpty( north ) if ( Puzzle.isEmpty( north )
&& shortest > stepsAway( x, y-1) ) && shortest > stepsAway(xy.down()) )
{ {
shortest = stepsAway( x, y-1); shortest = stepsAway(xy.down());
pref_x = x; pref.set(xy.down());
pref_y = y-1;
} }
if ( Puzzle.isEmpty( south ) if ( Puzzle.isEmpty( south )
&& shortest > stepsAway( x, y+1) ) && shortest > stepsAway(xy.up()) )
{ {
shortest = stepsAway( x, y+1); shortest = stepsAway(xy.up());
pref_x = x; pref.set(xy.up());
pref_y = y+1;
} }
if ( Puzzle.isEmpty( west ) if ( Puzzle.isEmpty( west )
&& shortest > stepsAway( x-1, y) ) && shortest > stepsAway(xy.left()) )
{ {
shortest = stepsAway( x-1, y); shortest = stepsAway(xy.left());
pref_x = x-1; pref.set(xy.left());
pref_y = y;
} }
if ( Puzzle.isEmpty( east ) if ( Puzzle.isEmpty( east )
&& shortest > stepsAway( x+1, y) ) && shortest > stepsAway( xy.right()) )
{ {
shortest = stepsAway( x+1, y); shortest = stepsAway( xy.right());
pref_x = x+1; pref.set(xy.right());
pref_y = y;
} }
// //
// Move the worker to the direction. If we cannot move worker // Move the worker to the direction. If we cannot move worker
// next to the box then we cannot select it. // next to the box then we cannot select it.
// //
if ( !puzzle.isValid(pref_x, pref_y) if ( !puzzle.isValid(pref)
|| !tryMoveWorker(animator, pref_x, pref_y) ) || !tryMoveWorker(animator, pref) )
return false; return false;
// //
// Select the box. // Select the box.
// //
select(animator,x,y); select(animator,xy);
} }
// //
// Done // Done
@@ -315,19 +307,25 @@ public class PuzzleLogic
return true; 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 ) protected void unselect( Animator animator )
@@ -340,9 +338,9 @@ public class PuzzleLogic
//animator.queue( new Animator.NoMove(x,y) ); //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. // 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 out of borders then nothing to do.
// //
if ( y < 0 || y >= puzzle.getRowCount() if ( !puzzle.isValid(xy) )
|| x < 0 || x >= puzzle.getColumnCount() )
return false; return false;
// //
// Check if the cell is a floor or goal. If yes then set the l // Check if the cell is a floor or goal. If yes then set the l
// if current value is greater than 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; return true;
} }
else else
@@ -389,105 +386,84 @@ public class PuzzleLogic
// For the beginning there is no cell in the list. // For the beginning there is no cell in the list.
// //
int front = 0; int front = 0;
int last = 0; setOfCells.removeAllElements();
// //
// Set the seed. // Set the seed.
// //
setMoves(puzzle.getWorkerX(),puzzle.getWorkerY(),0); setMoves(puzzle.getWorker(),0);
setOfCells[last++] = puzzle.getWorkerX(); setOfCells.add(puzzle.getWorker());
setOfCells[last++] = puzzle.getWorkerY();
// //
// Now on each loop pop one cell from the list and calculate the // Now on each loop pop one cell from the list and calculate the
// distance of cell around that cell. // distance of cell around that cell.
// //
while ( front < last ) while ( front < setOfCells.size() )
{ {
// //
// Pop the cell. // Pop the cell.
// //
int x = setOfCells[front++]; XYPair xy = setOfCells.elementAt(front++);
int y = setOfCells[front++];
// //
// Increase the length of cells all around given cell and push // Increase the length of cells all around given cell and push
// them into the list. // them into the list.
// //
int l = getMoves(x,y)+1; int l = getMoves(xy)+1;
if ( setMovesIfGreater(x-1,y,l) ) if ( setMovesIfGreater(xy.left(),l) ) {
{ setOfCells.add(xy.left());
setOfCells[last++] = x-1;
setOfCells[last++] = y;
} }
if ( setMovesIfGreater(x+1,y,l) ) if ( setMovesIfGreater(xy.right(),l) ) {
{ setOfCells.add(xy.right());
setOfCells[last++] = x+1;
setOfCells[last++] = y;
} }
if ( setMovesIfGreater(x,y-1,l) ) if ( setMovesIfGreater(xy.down(),l) ) {
{ setOfCells.add(xy.down());
setOfCells[last++] = x;
setOfCells[last++] = y-1;
} }
if ( setMovesIfGreater(x,y+1,l) ) if ( setMovesIfGreater(xy.up(),l) ) {
{ setOfCells.add(xy.up());
setOfCells[last++] = x;
setOfCells[last++] = y+1;
} }
} }
} }
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<XYPair> getDirections( XYPair xy )
{ {
int away = stepsAway(x,y); int away = stepsAway(xy);
if (away == Integer.MAX_VALUE) if (away == Integer.MAX_VALUE)
return null; return null;
// //
// Ok looks there is a routh to given cell. Now create an array // Ok looks there is a routh to given cell. Now create an array
// and fill in the step to get to the cell. // and fill in the step to get to the cell.
// //
int [] steps = new int[2*away]; Vector<XYPair> steps = new Vector<XYPair>(away);
int i = steps.length; steps.add(xy);
steps[--i] = y; while ( steps.size() < away )
steps[--i] = x;
while ( i > 0 )
{ {
x = steps[i]; xy = steps.lastElement();
y = steps[i+1]; int j = stepsAway(xy);
int j = stepsAway(x,y); if ( stepsAway(xy.left()) < j ) {
if ( stepsAway(x-1,y) < j ) steps.add(xy.left());
{
steps[--i] = y;
steps[--i] = x-1;
} }
else if ( stepsAway(x+1,y) < j ) else if ( stepsAway(xy.right()) < j ) {
{ steps.add(xy.right());
steps[--i] = y;
steps[--i] = x+1;
} }
else if ( stepsAway(x,y-1) < j ) else if ( stepsAway(xy.down()) < j ) {
{ steps.add(xy.down());
steps[--i] = y-1;
steps[--i] = x;
} }
else if ( stepsAway(x,y+1) < j ) else if ( stepsAway(xy.up()) < j ) {
{ steps.add(xy.up());
steps[--i] = y+1;
steps[--i] = x;
} }
} }
return steps; return steps;
} }
public int [] getPushDirections( int x, int y ) public XYPair [] getPushDirections( XYPair xy )
{ {
// //
// The selected box can be moved only orthogonally. // The selected box can be moved only orthogonally.
@@ -498,43 +474,35 @@ public class PuzzleLogic
// push direction is directed to x,y and is not opposit. In other words // push direction is directed to x,y and is not opposit. In other words
// selected->x,y and worker->selected are codirectional. // selected->x,y and worker->selected are codirectional.
// //
int selx = puzzle.getSelectedX(); XYPair sel = puzzle.getSelected();
int sely = puzzle.getSelectedY(); XYPair dist = XYPair.sub(xy, puzzle.getSelected());
int xx = x-selx; XYPair dir = XYPair.sub(puzzle.getSelected(), puzzle.getWorker());
int yy = y-sely; double scaliar = (double)(dist.x())*(double)(dir.x())
int sx = selx-puzzle.getWorkerX(); +(double)(dist.y())*(double)(dir.y());
int sy = sely-puzzle.getWorkerY(); int len = dist.l1_norm();
double scaliar = (double)(xx)*(double)(sx)
+(double)(yy)*(double)(sy);
int len = abs(xx)+abs(yy);
if ( scaliar != (double)len ) if ( scaliar != (double)len )
return null; return null;
// //
// Now check that all cell till x,y are free. // Now check that all cell till x,y are free.
// //
int ix = selx; XYPair ixy = new XYPair(puzzle.getSelected());
int iy = sely; while ( !xy.isEqual(ixy) )
while ( x != ix || y != iy )
{ {
ix+=sx; ixy = ixy.add(dir);
iy+=sy; if ( !puzzle.isEmpty(puzzle.getSym(ixy)) )
if ( !puzzle.isEmpty(puzzle.getSym(ix,iy)) )
return null; return null;
} }
// //
// Looks we could move the box till x,y. Create the steps array // Looks we could move the box till x,y. Create the steps array
// and fill it with push steps. // and fill it with push steps.
// //
int steps[] = new int [2*len]; XYPair steps[] = new XYPair[len];
int i = 0; int i = 0;
ix = selx; ixy.set(puzzle.getSelected());
iy = sely; while ( !xy.isEqual(ixy) )
while ( x != ix || y != iy )
{ {
steps[i++] = ix; steps[i++] = ixy;
steps[i++] = iy; ixy = ixy.add(dir);
ix+=sx;
iy+=sy;
} }
return steps; return steps;
} }

View File

@@ -1,6 +0,0 @@
package org.vostan.banvor.model;
public class Coord {
public int x;
public int y;
}

View File

@@ -1,14 +1,9 @@
package org.vostan.banvor.model; package org.vostan.banvor.model;
//import java.lang.Exception; //import java.lang.Exception;
import java.io.*;
import static java.lang.Math.*;
import android.util.Log;
import java.util.LinkedList; import java.util.LinkedList;
import static org.vostan.banvor.App.TAG;
public class Puzzle public class Puzzle
{ {
public final static int FLOOR = 0x0; public final static int FLOOR = 0x0;
@@ -18,28 +13,45 @@ public class Puzzle
public final static int BOX = 0x4; public final static int BOX = 0x4;
public final static int BINGO = BOX | GOAL; public final static int BINGO = BOX | GOAL;
public final static int WORKER = 0x8; public final static int WORKER = 0x8;
public final static int WORKER_NORTH = 0x00 | WORKER; public final static int WORKER_FACE_DOWN = 0x00 | WORKER;
public final static int WORKER_SOUTH = 0x10 | WORKER; public final static int WORKER_FACE_UP = 0x10 | WORKER;
public final static int WORKER_WEST = 0x20 | WORKER; public final static int WORKER_FACE_LEFT = 0x20 | WORKER;
public final static int WORKER_EAST = 0x30 | WORKER; public final static int WORKER_FACE_RIGHT = 0x30 | WORKER;
public final static int WORKER_MASK = WORKER_EAST | WORKER_WEST | WORKER_SOUTH | WORKER_NORTH; 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 SELECTED = 0x40;
public final static int MAX_COUNT = 0x80-1; public final static int MAX_COUNT = 0x80-1;
protected int columns; private final static XYPair unselected = new XYPair(-1,-1);
protected int rows; private final static XYPair zero = new XYPair(0,0);
protected int box_count;
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,37 +61,40 @@ public class Puzzle
public final int getColumnCount() public final int getColumnCount()
{ {
return columns; return board_size.x();
} }
public final int getRowCount() public final int getRowCount()
{ {
return rows; return board_size.y();
} }
public static int getSymCount() public static int getSymCount()
{ {
return MAX_COUNT; 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 ) public final int getX( int idx )
{ {
return idx % columns; return idx % board_size.x();
} }
public final int getY( int idx ) public final int getY( int idx )
{ {
return idx / columns; return idx / board_size.x();
} }
public final int getSym( int x, int y ) public final int getSym( XYPair c ) {
{ return board[getIndex(c)];
return board[getIndex(x,y)];
} }
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 ) public static boolean isEmpty( int v )
@@ -95,47 +110,33 @@ public class Puzzle
return (v & WORKER) != 0; return (v & WORKER) != 0;
} }
public final int getWorkerX() public final XYPair getWorker()
{ {
return worker_x; return worker;
}
public final int getWorkerY()
{
return worker_y;
} }
public final boolean isSelected() 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 ) public final boolean isOneStep( XYPair xy ) {
{ return isValid(xy) && XYPair.sub(worker, xy).l1_norm() == 1;
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 isDone() public final boolean isDone()
@@ -143,73 +144,42 @@ public class Puzzle
return bingos == box_count; 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. // 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. // Check that this is one step away.
// //
if ( !isOneStep(x,y) ) if ( !isOneStep(xy) )
return false; return false;
// //
// Check that this is empty space. // Check that this is empty space.
// //
int v = getSym(x,y); int v = getSym(xy);
if ( !isEmpty(v) ) if ( !isEmpty(v) )
return false; return false;
// //
@@ -218,38 +188,25 @@ public class Puzzle
if ( isSelected() ) if ( isSelected() )
unselect(); 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. // Move worker marker from current position to asked position.
// //
setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~WORKER_MASK); setSym(worker,getSym(worker)&~WORKER_MASK);
worker_x =x; worker.set(xy);
worker_y =y; setSym(worker,getSym(worker)|worker_direction(xy));
setSym(worker_x,worker_y,getSym(worker_x,worker_y)|worker);
return true; return true;
} }
public boolean select( int x, int y) public boolean select(XYPair xy)
{ {
// //
// Check that this is one step away. // Check that this is one step away.
// //
if ( !isOneStep(x,y) ) if ( !isOneStep(xy) )
return false; return false;
// //
// Check that this is empty space. // Check that this is empty space.
// //
int v = getSym(x,y); int v = getSym(xy);
if ( !hasBox(v) ) if ( !hasBox(v) )
return false; return false;
// //
@@ -258,24 +215,11 @@ public class Puzzle
if ( isSelected() ) if ( isSelected() )
unselect(); 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. // Move worker marker from current position to asked position.
// //
selected_x =x; selected.set(xy);
selected_y =y; setSym(worker,getSym(worker)&~WORKER_MASK|SELECTED|worker_direction(xy));
setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~WORKER_MASK|worker|SELECTED); setSym(selected,getSym(selected)|SELECTED);
setSym(selected_x,selected_y,getSym(selected_x,selected_y)|SELECTED);
return true; return true;
} }
@@ -289,14 +233,13 @@ public class Puzzle
// //
// Move worker marker from current position to asked position. // Move worker marker from current position to asked position.
// //
setSym(worker_x,worker_y,getSym(worker_x,worker_y)&~SELECTED); setSym(worker,getSym(worker)&~SELECTED);
setSym(selected_x,selected_y,getSym(selected_x,selected_y)&~SELECTED); setSym(selected,getSym(selected)&~SELECTED);
selected_x =-1; selected.set(unselected);
selected_y =-1;
return true; return true;
} }
public boolean push(int x, int y) public boolean push(XYPair xy)
{ {
// //
// If not selected then do nothing. // If not selected then do nothing.
@@ -306,30 +249,29 @@ public class Puzzle
// //
// Check that we go to the selected direction. // Check that we go to the selected direction.
// //
if ( selected_x != x && selected_y != y ) if ( !selected.isEqual(xy) )
return false; return false;
// //
// Check that this is a box that we move. // Check that this is a box that we move.
// //
int v = getSym(x,y); int v = getSym(xy);
if ( !hasBox(v) ) if ( !hasBox(v) )
return false; return false;
// //
// Check that the next space to the box is empty. // Check that the next space to the box is empty.
//w - 2*(w-x) = 2x -w //w - 2*(w-x) = 2x -w
int next_x = 2*x - worker_x; XYPair next = xy.mul(2).sub(worker);
int next_y = 2*y - worker_y; int next_v = getSym(next);
int next_v = getSym(next_x,next_y);
if ( !isEmpty(next_v) ) if ( !isEmpty(next_v) )
return false; return false;
// //
// Ok, looks we can move the box. Do it actually. // Ok, looks we can move the box. Do it actually.
// //
unselect(); unselect();
setSym(x,y,getSym(x,y)&~BOX); setSym(xy,getSym(xy)&~BOX);
setSym(next_x,next_y,getSym(next_x,next_y)|BOX); setSym(next,getSym(next)|BOX);
walk(x,y); walk(xy);
select(next_x,next_y); select(next);
// //
// Keep track of box count in place. // Keep track of box count in place.
// //
@@ -348,29 +290,23 @@ public class Puzzle
protected class State protected class State
{ {
public int [] board; public int [] board;
public int worker_x; public XYPair worker = new XYPair(0,0);
public int worker_y; public XYPair selected = new XYPair(0, 0);
public int selected_x; public int bingos = 0;
public int selected_y;
public int bingos;
public State(Puzzle puzzle) public State(Puzzle puzzle)
{ {
board = puzzle.board.clone(); board = puzzle.board.clone();
worker_x = puzzle.worker_x; worker.set(puzzle.worker);
worker_y = puzzle.worker_y; selected.set(puzzle.selected);
selected_x = puzzle.selected_x;
selected_y = puzzle.selected_y;
bingos = puzzle.bingos; bingos = puzzle.bingos;
} }
public void restore(Puzzle puzzle) public void restore(Puzzle puzzle)
{ {
puzzle.board = board; puzzle.board = board;
puzzle.worker_x = worker_x; puzzle.worker.set(worker);
puzzle.worker_y = worker_y; puzzle.selected.set(selected);
puzzle.selected_x = selected_x;
puzzle.selected_y = selected_y;
puzzle.bingos = bingos; puzzle.bingos = bingos;
} }
} }

View File

@@ -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;
}
}

View File

@@ -15,63 +15,20 @@ public class PuzzleContainer implements IPuzzleSource
* Load puzzle from the puzzle.bin. * Load puzzle from the puzzle.bin.
*/ */
public Puzzle getPuzzle( int i ) 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() ); InputStream is = theApp().getResources().openRawResource(R.raw.puzzles);
e.printStackTrace(); return PuzzleBinLoader.loadNthPuzzle(is, i);
return null; }
}}
// //
// Retrive amount of puzzles. // Retrieve amount of puzzles.
// //
public int getCount() public int getCount()
{try{ {
if ( count == 0 ) if ( count == 0 )
{ {
InputStream is = theApp().getResources().openRawResource(R.raw.puzzles); InputStream is = theApp().getResources().openRawResource(R.raw.puzzles);
count = read_i32(is); count = PuzzleBinLoader.getCount(is);
} }
return count; 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;
}
} }

View File

@@ -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);
}
}

View File

@@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

View File

@@ -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());
}
}

View File

@@ -3,6 +3,10 @@ buildscript {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
//This is needed for SVG->PNG
// maven {
// url "https://plugins.gradle.org/m2/"
// }
} }
dependencies { dependencies {
classpath "com.android.tools.build:gradle:7.0.2" classpath "com.android.tools.build:gradle:7.0.2"
@@ -10,6 +14,9 @@ buildscript {
def nav_version = "2.3.5" def nav_version = "2.3.5"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" 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 // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
} }