More refactoring: PuzzlyRouteFinder is seperated, Animator dependency is reversed.
This commit is contained in:
@@ -2,11 +2,9 @@ package org.vostan.banvor;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.vostan.banvor.game.State;
|
import org.vostan.banvor.game.State;
|
||||||
import org.vostan.banvor.model.PuzzleContainer;
|
import org.vostan.banvor.game.PuzzleContainer;
|
||||||
import org.vostan.banvor.model.IPuzzleSource;
|
|
||||||
|
|
||||||
public class App extends Application
|
public class App extends Application
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ public class Animator implements Runnable
|
|||||||
|
|
||||||
protected static abstract class XYAction extends Action
|
protected static abstract class XYAction extends Action
|
||||||
{
|
{
|
||||||
protected XYPair _xy;
|
protected XYPair _xy = new XYPair();
|
||||||
|
|
||||||
public XYAction( XYPair xy ){
|
public XYAction( XYPair xy ){
|
||||||
_xy = xy;
|
_xy.set(xy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.vostan.banvor.board;
|
||||||
|
|
||||||
|
import org.vostan.banvor.model.IPuzzleAnimator;
|
||||||
|
import org.vostan.banvor.model.XYPair;
|
||||||
|
|
||||||
|
public class PuzzleAnimator implements IPuzzleAnimator {
|
||||||
|
|
||||||
|
Animator animator = null;
|
||||||
|
|
||||||
|
public PuzzleAnimator(Animator a){
|
||||||
|
animator = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void move(XYPair xy )
|
||||||
|
{
|
||||||
|
animator.queue( new Animator.Move(xy) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void push(XYPair xy )
|
||||||
|
{
|
||||||
|
animator.queue( new Animator.Push(xy) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void select(XYPair xy )
|
||||||
|
{
|
||||||
|
animator.queue( new Animator.Select(xy) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unselect()
|
||||||
|
{
|
||||||
|
animator.queue( new Animator.Unselect() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buzz()
|
||||||
|
{
|
||||||
|
//animator.queue( new Animator.NoMove(x,y) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void undoable(XYPair xy )
|
||||||
|
{
|
||||||
|
animator.queue( new Animator.NoMove(xy) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import android.view.GestureDetector;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.ScaleGestureDetector;
|
import android.view.ScaleGestureDetector;
|
||||||
|
|
||||||
import org.vostan.banvor.game.PuzzleLogic;
|
import org.vostan.banvor.model.PuzzleChoreographer;
|
||||||
import org.vostan.banvor.model.Puzzle;
|
import org.vostan.banvor.model.Puzzle;
|
||||||
import org.vostan.banvor.model.XYPair;
|
import org.vostan.banvor.model.XYPair;
|
||||||
|
|
||||||
@@ -29,12 +29,13 @@ public class PuzzleControl extends PuzzleView
|
|||||||
{
|
{
|
||||||
protected GestureDetector simpled;
|
protected GestureDetector simpled;
|
||||||
protected ScaleGestureDetector scaled;
|
protected ScaleGestureDetector scaled;
|
||||||
protected PuzzleLogic logic;
|
protected PuzzleChoreographer logic;
|
||||||
protected Animator animator;
|
protected Animator animator;
|
||||||
|
protected PuzzleAnimator puzzleAnimator;
|
||||||
protected PuzzleControlLister lister;
|
protected PuzzleControlLister lister;
|
||||||
|
|
||||||
protected float lastSpan;
|
protected float lastSpan;
|
||||||
protected Point singleTapTile = new Point();
|
protected XYPair singleTapTile = new XYPair();
|
||||||
|
|
||||||
public interface PuzzleControlLister
|
public interface PuzzleControlLister
|
||||||
{
|
{
|
||||||
@@ -46,9 +47,10 @@ public class PuzzleControl extends PuzzleView
|
|||||||
public PuzzleControl(Context c, AttributeSet attributeSet)
|
public PuzzleControl(Context c, AttributeSet attributeSet)
|
||||||
{
|
{
|
||||||
super(c,attributeSet);
|
super(c,attributeSet);
|
||||||
logic = new PuzzleLogic();
|
logic = new PuzzleChoreographer();
|
||||||
animator = new Animator( this );
|
animator = new Animator( this );
|
||||||
animator.setAnimationLister(this);
|
animator.setAnimationLister(this);
|
||||||
|
puzzleAnimator = new PuzzleAnimator(animator);
|
||||||
|
|
||||||
simpled = new GestureDetector( c, this );
|
simpled = new GestureDetector( c, this );
|
||||||
simpled.setOnDoubleTapListener(this);
|
simpled.setOnDoubleTapListener(this);
|
||||||
@@ -118,12 +120,12 @@ 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(new XYPair(singleTapTile.x, singleTapTile.y) ))
|
if ( !puzzle.isValid(singleTapTile) )
|
||||||
return true;
|
return true;
|
||||||
//
|
//
|
||||||
// Create sequence of steps and then animate it.
|
// Create sequence of steps and then animate it.
|
||||||
//
|
//
|
||||||
if ( logic.createSteps(animator, singleTapTile.x, singleTapTile.y) )
|
if ( logic.createSteps(puzzleAnimator, singleTapTile))
|
||||||
{
|
{
|
||||||
puzzle.save();
|
puzzle.save();
|
||||||
animator.play();
|
animator.play();
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ public class PuzzleView extends View
|
|||||||
* Apply screen to board mapping and if the points are in the
|
* Apply screen to board mapping and if the points are in the
|
||||||
* column,row range then return it. Otherwise return null.
|
* column,row range then return it. Otherwise return null.
|
||||||
*/
|
*/
|
||||||
public void getTile( float scr_x, float scr_y, Point /*out*/ tile )
|
public void getTile(float scr_x, float scr_y, XYPair /*out*/ tile )
|
||||||
{
|
{
|
||||||
float [] scr_p = { scr_x, scr_y };
|
float [] scr_p = { scr_x, scr_y };
|
||||||
scr2col.mapPoints( scr_p );
|
scr2col.mapPoints( scr_p );
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.vostan.banvor.model;
|
package org.vostan.banvor.game;
|
||||||
|
|
||||||
import static org.vostan.banvor.App.TAG;
|
import static org.vostan.banvor.App.TAG;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.vostan.banvor.model.Puzzle;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
package org.vostan.banvor.model;
|
package org.vostan.banvor.game;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.IOException;
|
|
||||||
import static org.vostan.banvor.App.theApp;
|
import static org.vostan.banvor.App.theApp;
|
||||||
import org.vostan.banvor.model.Puzzle;
|
|
||||||
|
import org.vostan.banvor.game.PuzzleBinLoader;
|
||||||
|
|
||||||
import org.vostan.banvor.R;
|
import org.vostan.banvor.R;
|
||||||
|
import org.vostan.banvor.model.IPuzzleSource;
|
||||||
|
import org.vostan.banvor.model.Puzzle;
|
||||||
|
|
||||||
public class PuzzleContainer implements IPuzzleSource
|
public class PuzzleContainer implements IPuzzleSource
|
||||||
{
|
{
|
||||||
@@ -1,509 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this template, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class PuzzleLogic
|
|
||||||
{
|
|
||||||
//protected PlayActivity activity;
|
|
||||||
protected Puzzle puzzle = null;
|
|
||||||
protected int [] moves;
|
|
||||||
protected Vector<XYPair> setOfCells = new Vector<XYPair>();
|
|
||||||
|
|
||||||
public PuzzleLogic()
|
|
||||||
{}
|
|
||||||
|
|
||||||
public void setPuzzle( Puzzle p )
|
|
||||||
{
|
|
||||||
puzzle = p;
|
|
||||||
moves = new int[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(xy) )
|
|
||||||
return false;
|
|
||||||
//
|
|
||||||
// Now check what tile was tapped.
|
|
||||||
// If the tapped is a floor then ...
|
|
||||||
//
|
|
||||||
int tile = puzzle.getSym(xy);
|
|
||||||
if ( Puzzle.isEmpty(tile) )
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Calculate possible moves map.
|
|
||||||
//
|
|
||||||
calcMoves();
|
|
||||||
//
|
|
||||||
// Check if worker selected a box and can push it to the location?
|
|
||||||
// If yes then we are done.
|
|
||||||
//
|
|
||||||
if ( puzzle.isSelected()
|
|
||||||
&& 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, xy ) )
|
|
||||||
return true;
|
|
||||||
//
|
|
||||||
// Show that action is not allowed.
|
|
||||||
//
|
|
||||||
undoable(animator, xy);
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// The tapped is the worker. Try move the box. If not possible
|
|
||||||
// then unselect if selected.
|
|
||||||
//
|
|
||||||
else if ( Puzzle.hasWorker(tile) )
|
|
||||||
{
|
|
||||||
if ( !puzzle.isSelected() )
|
|
||||||
{
|
|
||||||
buzz( animator );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Calculate possible moves map.
|
|
||||||
//
|
|
||||||
calcMoves();
|
|
||||||
//
|
|
||||||
// Check if worker selected a box and can push it to the location?
|
|
||||||
// If yes then we are done.
|
|
||||||
//
|
|
||||||
if ( tryMoveBox(animator, xy) )
|
|
||||||
return true;
|
|
||||||
//
|
|
||||||
// If the box is not movable then unselect it.
|
|
||||||
//
|
|
||||||
unselect( animator );
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// The tapped is a box.
|
|
||||||
//
|
|
||||||
else if ( Puzzle.hasBox(tile) )
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Calculate possible moves map.
|
|
||||||
//
|
|
||||||
calcMoves();
|
|
||||||
//
|
|
||||||
// If the box is selected then unselect it.
|
|
||||||
//
|
|
||||||
if ( puzzle.isSelected(xy) )
|
|
||||||
{
|
|
||||||
unselect( animator );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Try move the worker next to the box and select it if the
|
|
||||||
// box is not selected yet.
|
|
||||||
//
|
|
||||||
if ( trySelectBox( animator, xy ) )
|
|
||||||
return true;
|
|
||||||
//
|
|
||||||
// Show that action is not allowed if reached till here.
|
|
||||||
//
|
|
||||||
undoable( animator, xy );
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Routes to accessible cells from where worker stands.
|
|
||||||
//
|
|
||||||
protected boolean tryMoveWorker( Animator animator, XYPair xy )
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// If the filed is not accessable then move failed.
|
|
||||||
//
|
|
||||||
if ( !isAccessible(xy) )
|
|
||||||
return false;
|
|
||||||
//
|
|
||||||
// First unselect box.
|
|
||||||
//
|
|
||||||
if ( puzzle.isSelected() )
|
|
||||||
unselect( animator );
|
|
||||||
//
|
|
||||||
// Get directions and queue moves accordingly.
|
|
||||||
//
|
|
||||||
Vector<XYPair> dirs = getDirections(xy);
|
|
||||||
ListIterator<XYPair> it = dirs.listIterator(dirs.size());
|
|
||||||
while ( it.hasPrevious() ) {
|
|
||||||
move(animator, it.previous());
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Done.
|
|
||||||
//
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean tryMoveBox( Animator animator, XYPair xy )
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// If no box is selected then we cannot move no box.
|
|
||||||
//
|
|
||||||
if ( !puzzle.isSelected() )
|
|
||||||
return false;
|
|
||||||
//
|
|
||||||
// Check that asked move is orthogonal to the slected box.
|
|
||||||
//
|
|
||||||
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 ( dxy.isEqual(XYPair.ZERO) )
|
|
||||||
return false;
|
|
||||||
//
|
|
||||||
// Now find the desired place for the worker to start push this
|
|
||||||
// box.
|
|
||||||
//
|
|
||||||
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
|
|
||||||
w = w.down();
|
|
||||||
//
|
|
||||||
// Check if the desired place for the worker is accessable? If not
|
|
||||||
// then there is no point to continue.
|
|
||||||
//
|
|
||||||
if ( !isAccessible(w) )
|
|
||||||
return false;
|
|
||||||
//
|
|
||||||
// Now check that all cell till x,y are empty and that we can
|
|
||||||
// push box till there.
|
|
||||||
//
|
|
||||||
XYPair step_xy = box_xy.sub(w);
|
|
||||||
for ( XYPair i = box_xy; !xy.isEqual(i); )
|
|
||||||
{
|
|
||||||
i = i.add(step_xy);
|
|
||||||
int v = puzzle.getSym(i);
|
|
||||||
if ( !puzzle.isEmpty(v) && !puzzle.hasWorker(v) )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Ok, looks we can do the desired action. Now put instructions on
|
|
||||||
// what to do. First move worker to desired position if he is not
|
|
||||||
// there already.
|
|
||||||
//
|
|
||||||
if ( !w.isEqual(puzzle.getWorker()) )
|
|
||||||
{
|
|
||||||
tryMoveWorker( animator, w );
|
|
||||||
select( animator, box_xy );
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Now create the steps to push the box.
|
|
||||||
//
|
|
||||||
for ( XYPair i = box_xy; !i.isEqual(xy); i = i.add(step_xy) ) {
|
|
||||||
push( animator, i );
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Done
|
|
||||||
//
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean trySelectBox( Animator animator, XYPair xy )
|
|
||||||
{
|
|
||||||
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
|
|
||||||
// then do nothing and if othe box is selected then unselect it first
|
|
||||||
// and then select the box.
|
|
||||||
//
|
|
||||||
if ( Puzzle.hasWorker( west )
|
|
||||||
|| Puzzle.hasWorker( east )
|
|
||||||
|| Puzzle.hasWorker( north )
|
|
||||||
|| Puzzle.hasWorker( south ) )
|
|
||||||
{
|
|
||||||
if ( !puzzle.isSelected(xy) )
|
|
||||||
{
|
|
||||||
if ( puzzle.isSelected() )
|
|
||||||
unselect( animator );
|
|
||||||
select( animator, xy );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Otherwise, check which is of the cells is in closes walking
|
|
||||||
// distance and move worker to that cell, then select.
|
|
||||||
//
|
|
||||||
else
|
|
||||||
{
|
|
||||||
XYPair pref = new XYPair(-1, -1);
|
|
||||||
int shortest = Integer.MAX_VALUE;
|
|
||||||
if ( Puzzle.isEmpty( north )
|
|
||||||
&& shortest > stepsAway(xy.down()) )
|
|
||||||
{
|
|
||||||
shortest = stepsAway(xy.down());
|
|
||||||
pref.set(xy.down());
|
|
||||||
}
|
|
||||||
if ( Puzzle.isEmpty( south )
|
|
||||||
&& shortest > stepsAway(xy.up()) )
|
|
||||||
{
|
|
||||||
shortest = stepsAway(xy.up());
|
|
||||||
pref.set(xy.up());
|
|
||||||
}
|
|
||||||
if ( Puzzle.isEmpty( west )
|
|
||||||
&& shortest > stepsAway(xy.left()) )
|
|
||||||
{
|
|
||||||
shortest = stepsAway(xy.left());
|
|
||||||
pref.set(xy.left());
|
|
||||||
}
|
|
||||||
if ( Puzzle.isEmpty( east )
|
|
||||||
&& shortest > stepsAway( xy.right()) )
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
|| !tryMoveWorker(animator, pref) )
|
|
||||||
return false;
|
|
||||||
//
|
|
||||||
// Select the box.
|
|
||||||
//
|
|
||||||
select(animator,xy);
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Done
|
|
||||||
//
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// These should move to a callback.
|
|
||||||
//
|
|
||||||
|
|
||||||
protected void move( Animator animator, XYPair xy )
|
|
||||||
{
|
|
||||||
animator.queue( new Animator.Move(xy) );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void push( Animator animator, XYPair xy )
|
|
||||||
{
|
|
||||||
animator.queue( new Animator.Push(xy) );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void select( Animator animator, XYPair xy )
|
|
||||||
{
|
|
||||||
animator.queue( new Animator.Select(xy) );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void unselect( Animator animator )
|
|
||||||
{
|
|
||||||
animator.queue( new Animator.Unselect() );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void buzz( Animator animator )
|
|
||||||
{
|
|
||||||
//animator.queue( new Animator.NoMove(x,y) );
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void undoable( Animator animator, XYPair xy )
|
|
||||||
{
|
|
||||||
animator.queue( new Animator.NoMove(xy) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Routes to accessible cells from where worker stands.
|
|
||||||
//
|
|
||||||
|
|
||||||
protected final int getMoves( XYPair xy )
|
|
||||||
{
|
|
||||||
return moves[puzzle.getIndex(xy)];
|
|
||||||
}
|
|
||||||
protected void setMoves( XYPair xy, int v )
|
|
||||||
{
|
|
||||||
moves[puzzle.getIndex(xy)]=v;
|
|
||||||
}
|
|
||||||
private boolean setMovesIfGreater( XYPair xy, int l )
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// If out of borders then nothing to do.
|
|
||||||
//
|
|
||||||
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(xy) > l && puzzle.isEmpty(puzzle.getSym(xy)))
|
|
||||||
{
|
|
||||||
setMoves(xy,l);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void calcMoves()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Erase moves array.
|
|
||||||
//
|
|
||||||
Arrays.fill(moves,Integer.MAX_VALUE);
|
|
||||||
//
|
|
||||||
// For the beginning there is no cell in the list.
|
|
||||||
//
|
|
||||||
int front = 0;
|
|
||||||
setOfCells.removeAllElements();
|
|
||||||
//
|
|
||||||
// Set the seed.
|
|
||||||
//
|
|
||||||
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 < setOfCells.size() )
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Pop the cell.
|
|
||||||
//
|
|
||||||
XYPair xy = setOfCells.elementAt(front++);
|
|
||||||
//
|
|
||||||
// Increase the length of cells all around given cell and push
|
|
||||||
// them into the list.
|
|
||||||
//
|
|
||||||
int l = getMoves(xy)+1;
|
|
||||||
if ( setMovesIfGreater(xy.left(),l) ) {
|
|
||||||
setOfCells.add(xy.left());
|
|
||||||
}
|
|
||||||
if ( setMovesIfGreater(xy.right(),l) ) {
|
|
||||||
setOfCells.add(xy.right());
|
|
||||||
}
|
|
||||||
if ( setMovesIfGreater(xy.down(),l) ) {
|
|
||||||
setOfCells.add(xy.down());
|
|
||||||
}
|
|
||||||
if ( setMovesIfGreater(xy.up(),l) ) {
|
|
||||||
setOfCells.add(xy.up());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int stepsAway( XYPair xy )
|
|
||||||
{
|
|
||||||
return getMoves(xy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean isAccessible( XYPair xy )
|
|
||||||
{
|
|
||||||
return puzzle.isValid(xy) && stepsAway(xy) != Integer.MAX_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector<XYPair> getDirections( XYPair xy )
|
|
||||||
{
|
|
||||||
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.
|
|
||||||
//
|
|
||||||
Vector<XYPair> steps = new Vector<XYPair>(away);
|
|
||||||
steps.add(xy);
|
|
||||||
while ( steps.size() < away )
|
|
||||||
{
|
|
||||||
xy = steps.lastElement();
|
|
||||||
int j = stepsAway(xy);
|
|
||||||
if ( stepsAway(xy.left()) < j ) {
|
|
||||||
steps.add(xy.left());
|
|
||||||
}
|
|
||||||
else if ( stepsAway(xy.right()) < j ) {
|
|
||||||
steps.add(xy.right());
|
|
||||||
}
|
|
||||||
else if ( stepsAway(xy.down()) < j ) {
|
|
||||||
steps.add(xy.down());
|
|
||||||
}
|
|
||||||
else if ( stepsAway(xy.up()) < j ) {
|
|
||||||
steps.add(xy.up());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return steps;
|
|
||||||
}
|
|
||||||
|
|
||||||
public XYPair [] getPushDirections( XYPair xy )
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// The selected box can be moved only orthogonally.
|
|
||||||
// Check that worker is on opposite side of the box.
|
|
||||||
// Statement:
|
|
||||||
// If scaliar product of selected->x,y and worker->selected is equal
|
|
||||||
// to manhatten distance of x,y from the selected box then the worker
|
|
||||||
// push direction is directed to x,y and is not opposit. In other words
|
|
||||||
// selected->x,y and worker->selected are codirectional.
|
|
||||||
//
|
|
||||||
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.
|
|
||||||
//
|
|
||||||
XYPair ixy = new XYPair(puzzle.getSelected());
|
|
||||||
while ( !xy.isEqual(ixy) )
|
|
||||||
{
|
|
||||||
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.
|
|
||||||
//
|
|
||||||
XYPair steps[] = new XYPair[len];
|
|
||||||
int i = 0;
|
|
||||||
ixy.set(puzzle.getSelected());
|
|
||||||
while ( !xy.isEqual(ixy) )
|
|
||||||
{
|
|
||||||
steps[i++] = ixy;
|
|
||||||
ixy = ixy.add(dir);
|
|
||||||
}
|
|
||||||
return steps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.vostan.banvor.model;
|
||||||
|
|
||||||
|
public interface IPuzzleAnimator {
|
||||||
|
public void move(XYPair xy );
|
||||||
|
public void push( XYPair xy );
|
||||||
|
public void select( XYPair xy );
|
||||||
|
public void unselect();
|
||||||
|
public void buzz();
|
||||||
|
public void undoable( XYPair xy );
|
||||||
|
}
|
||||||
@@ -21,8 +21,7 @@ public class Puzzle
|
|||||||
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;
|
||||||
|
|
||||||
private final static XYPair unselected = new XYPair(-1,-1);
|
private final static XYPair UNSELECTED = new XYPair(-1,-1);
|
||||||
private final static XYPair zero = new XYPair(0,0);
|
|
||||||
|
|
||||||
|
|
||||||
private int [] board;
|
private int [] board;
|
||||||
@@ -36,7 +35,7 @@ public class Puzzle
|
|||||||
}
|
}
|
||||||
public Puzzle(int cols, int rows, int [] b)
|
public Puzzle(int cols, int rows, int [] b)
|
||||||
{
|
{
|
||||||
board_size.set(new XYPair(cols, rows));
|
board_size.set(cols, rows);
|
||||||
box_count = 0;
|
box_count = 0;
|
||||||
bingos = 0;
|
bingos = 0;
|
||||||
// board = new int[board_size.x()*board_size.y()];
|
// board = new int[board_size.x()*board_size.y()];
|
||||||
@@ -72,27 +71,17 @@ public class Puzzle
|
|||||||
return MAX_COUNT;
|
return MAX_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getIndex( XYPair c )
|
public final int getIndex( final XYPair c ) {
|
||||||
{
|
|
||||||
return c.y()*board_size.x()+c.x();
|
return c.y()*board_size.x()+c.x();
|
||||||
}
|
}
|
||||||
public final XYPair getXY( int idx )
|
public final XYPair getXY( int idx ) {
|
||||||
{
|
return new XYPair(idx % board_size.x(), idx / board_size.x());
|
||||||
return new XYPair(getX(idx), getY(idx));
|
|
||||||
}
|
|
||||||
public final int getX( int idx )
|
|
||||||
{
|
|
||||||
return idx % board_size.x();
|
|
||||||
}
|
|
||||||
public final int getY( int idx )
|
|
||||||
{
|
|
||||||
return idx / board_size.x();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getSym( XYPair c ) {
|
public final int getSym( XYPair c ) {
|
||||||
return board[getIndex(c)];
|
return board[getIndex(c)];
|
||||||
}
|
}
|
||||||
public void setSym( XYPair xy, int v )
|
public void setSym( final XYPair xy, int v )
|
||||||
{
|
{
|
||||||
board[getIndex(xy)]=v;
|
board[getIndex(xy)]=v;
|
||||||
}
|
}
|
||||||
@@ -110,32 +99,27 @@ public class Puzzle
|
|||||||
return (v & WORKER) != 0;
|
return (v & WORKER) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final XYPair getWorker()
|
public final XYPair getWorker() {
|
||||||
{
|
return new XYPair(worker);
|
||||||
return worker;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isSelected()
|
public final boolean isSelected()
|
||||||
{
|
{
|
||||||
return !selected.isEqual(unselected);
|
return !selected.isEqual(UNSELECTED);
|
||||||
}
|
}
|
||||||
|
public final boolean isSelected( final XYPair s ) {
|
||||||
public final boolean isSelected( XYPair s )
|
|
||||||
{
|
|
||||||
return selected.isEqual(s);
|
return selected.isEqual(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final XYPair getSelected()
|
public final XYPair getSelected()
|
||||||
{
|
{
|
||||||
return selected;
|
return new XYPair(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isValid( XYPair xy )
|
public final boolean isValid( final XYPair xy ) {
|
||||||
{
|
return xy.isInside(XYPair.ZERO,board_size);
|
||||||
return xy.isInside(zero,board_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isOneStep( XYPair xy ) {
|
public final boolean isOneStep( final XYPair xy ) {
|
||||||
return isValid(xy) && XYPair.sub(worker, xy).l1_norm() == 1;
|
return isValid(xy) && XYPair.sub(worker, xy).l1_norm() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +133,7 @@ public class Puzzle
|
|||||||
// Move worker.
|
// Move worker.
|
||||||
//
|
//
|
||||||
|
|
||||||
private int worker_direction(XYPair xy) {
|
private final int worker_direction(XYPair xy) {
|
||||||
//
|
//
|
||||||
// Find direction to move.
|
// Find direction to move.
|
||||||
//
|
//
|
||||||
@@ -235,11 +219,11 @@ public class Puzzle
|
|||||||
//
|
//
|
||||||
setSym(worker,getSym(worker)&~SELECTED);
|
setSym(worker,getSym(worker)&~SELECTED);
|
||||||
setSym(selected,getSym(selected)&~SELECTED);
|
setSym(selected,getSym(selected)&~SELECTED);
|
||||||
selected.set(unselected);
|
selected.set(UNSELECTED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean push(XYPair xy)
|
public boolean push(final XYPair xy)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// If not selected then do nothing.
|
// If not selected then do nothing.
|
||||||
|
|||||||
@@ -0,0 +1,301 @@
|
|||||||
|
/*
|
||||||
|
* To change this template, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package org.vostan.banvor.model;
|
||||||
|
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import org.vostan.banvor.model.IPuzzleAnimator;
|
||||||
|
import org.vostan.banvor.model.Puzzle;
|
||||||
|
import org.vostan.banvor.model.PuzzleRouteFinder;
|
||||||
|
import org.vostan.banvor.model.XYPair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PuzzleChoreographer
|
||||||
|
{
|
||||||
|
protected Puzzle puzzle = null;
|
||||||
|
protected PuzzleRouteFinder routeFinder;
|
||||||
|
|
||||||
|
public PuzzleChoreographer()
|
||||||
|
{}
|
||||||
|
|
||||||
|
public void setPuzzle( Puzzle p )
|
||||||
|
{
|
||||||
|
puzzle = p;
|
||||||
|
routeFinder = new PuzzleRouteFinder(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean createSteps(IPuzzleAnimator animator, XYPair xy )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Check that the x,y are valid.
|
||||||
|
//
|
||||||
|
if ( !puzzle.isValid(xy) )
|
||||||
|
return false;
|
||||||
|
//
|
||||||
|
// Now check what tile was tapped.
|
||||||
|
// If the tapped is a floor then ...
|
||||||
|
//
|
||||||
|
int tile = puzzle.getSym(xy);
|
||||||
|
if ( Puzzle.isEmpty(tile) )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Calculate possible moves map.
|
||||||
|
//
|
||||||
|
routeFinder.calcMoves();
|
||||||
|
//
|
||||||
|
// Check if worker selected a box and can push it to the location?
|
||||||
|
// If yes then we are done.
|
||||||
|
//
|
||||||
|
if ( puzzle.isSelected()
|
||||||
|
&& 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, xy ) )
|
||||||
|
return true;
|
||||||
|
//
|
||||||
|
// Show that action is not allowed.
|
||||||
|
//
|
||||||
|
animator.undoable(xy);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// The tapped is the worker. Try move the box. If not possible
|
||||||
|
// then unselect if selected.
|
||||||
|
//
|
||||||
|
else if ( Puzzle.hasWorker(tile) )
|
||||||
|
{
|
||||||
|
if ( !puzzle.isSelected() )
|
||||||
|
{
|
||||||
|
animator.buzz();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Calculate possible moves map.
|
||||||
|
//
|
||||||
|
routeFinder.calcMoves();
|
||||||
|
//
|
||||||
|
// Check if worker selected a box and can push it to the location?
|
||||||
|
// If yes then we are done.
|
||||||
|
//
|
||||||
|
if ( tryMoveBox(animator, xy) )
|
||||||
|
return true;
|
||||||
|
//
|
||||||
|
// If the box is not movable then unselect it.
|
||||||
|
//
|
||||||
|
animator.unselect();
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// The tapped is a box.
|
||||||
|
//
|
||||||
|
else if ( Puzzle.hasBox(tile) )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Calculate possible moves map.
|
||||||
|
//
|
||||||
|
routeFinder.calcMoves();
|
||||||
|
//
|
||||||
|
// If the box is selected then unselect it.
|
||||||
|
//
|
||||||
|
if ( puzzle.isSelected(xy) )
|
||||||
|
{
|
||||||
|
animator.unselect();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Try move the worker next to the box and select it if the
|
||||||
|
// box is not selected yet.
|
||||||
|
//
|
||||||
|
if ( trySelectBox( animator, xy ) )
|
||||||
|
return true;
|
||||||
|
//
|
||||||
|
// Show that action is not allowed if reached till here.
|
||||||
|
//
|
||||||
|
animator.undoable(xy);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Routes to accessible cells from where worker stands.
|
||||||
|
//
|
||||||
|
protected boolean tryMoveWorker( IPuzzleAnimator animator, XYPair xy )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// If the filed is not accessable then move failed.
|
||||||
|
//
|
||||||
|
if ( !routeFinder.isAccessible(xy) )
|
||||||
|
return false;
|
||||||
|
//
|
||||||
|
// First unselect box.
|
||||||
|
//
|
||||||
|
if ( puzzle.isSelected() )
|
||||||
|
animator.unselect();
|
||||||
|
//
|
||||||
|
// Get directions and queue moves accordingly.
|
||||||
|
//
|
||||||
|
Vector<XYPair> dirs = routeFinder.getDirections(xy);
|
||||||
|
ListIterator<XYPair> it = dirs.listIterator(dirs.size());
|
||||||
|
while ( it.hasPrevious() ) {
|
||||||
|
animator.move(it.previous());
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Done.
|
||||||
|
//
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean tryMoveBox( IPuzzleAnimator animator, XYPair xy )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// If no box is selected then we cannot move no box.
|
||||||
|
//
|
||||||
|
if ( !puzzle.isSelected() )
|
||||||
|
return false;
|
||||||
|
//
|
||||||
|
// Check that asked move is orthogonal to the slected box.
|
||||||
|
//
|
||||||
|
final 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 ( dxy.isEqual(XYPair.ZERO) )
|
||||||
|
return false;
|
||||||
|
//
|
||||||
|
// Now find the desired place for the worker to start push this
|
||||||
|
// box.
|
||||||
|
//
|
||||||
|
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
|
||||||
|
w = w.down();
|
||||||
|
//
|
||||||
|
// Check if the desired place for the worker is accessable? If not
|
||||||
|
// then there is no point to continue.
|
||||||
|
//
|
||||||
|
if ( !routeFinder.isAccessible(w) )
|
||||||
|
return false;
|
||||||
|
//
|
||||||
|
// Now check that all cell till x,y are empty and that we can
|
||||||
|
// push box till there.
|
||||||
|
//
|
||||||
|
XYPair step_xy = box_xy.sub(w);
|
||||||
|
for ( XYPair i = box_xy; !xy.isEqual(i); )
|
||||||
|
{
|
||||||
|
i = i.add(step_xy);
|
||||||
|
int v = puzzle.getSym(i);
|
||||||
|
if ( !puzzle.isEmpty(v) && !puzzle.hasWorker(v) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Ok, looks we can do the desired action. Now put instructions on
|
||||||
|
// what to do. First move worker to desired position if he is not
|
||||||
|
// there already.
|
||||||
|
//
|
||||||
|
if ( !w.isEqual(puzzle.getWorker()) )
|
||||||
|
{
|
||||||
|
tryMoveWorker( animator, w );
|
||||||
|
animator.select(box_xy);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Now create the steps to push the box.
|
||||||
|
//
|
||||||
|
for ( XYPair i = box_xy; !i.isEqual(xy); i = i.add(step_xy) ) {
|
||||||
|
animator.push(i);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Done
|
||||||
|
//
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean trySelectBox( IPuzzleAnimator animator, XYPair xy )
|
||||||
|
{
|
||||||
|
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
|
||||||
|
// then do nothing and if othe box is selected then unselect it first
|
||||||
|
// and then select the box.
|
||||||
|
//
|
||||||
|
if ( Puzzle.hasWorker( west )
|
||||||
|
|| Puzzle.hasWorker( east )
|
||||||
|
|| Puzzle.hasWorker( north )
|
||||||
|
|| Puzzle.hasWorker( south ) )
|
||||||
|
{
|
||||||
|
if ( !puzzle.isSelected(xy) )
|
||||||
|
{
|
||||||
|
if ( puzzle.isSelected() )
|
||||||
|
animator.unselect();
|
||||||
|
animator.select(xy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Otherwise, check which is of the cells is in closes walking
|
||||||
|
// distance and move worker to that cell, then select.
|
||||||
|
//
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XYPair pref = new XYPair(-1, -1);
|
||||||
|
int shortest = Integer.MAX_VALUE;
|
||||||
|
if ( Puzzle.isEmpty( north )
|
||||||
|
&& shortest > routeFinder.stepsAway(xy.down()) )
|
||||||
|
{
|
||||||
|
shortest = routeFinder.stepsAway(xy.down());
|
||||||
|
pref.set(xy.down());
|
||||||
|
}
|
||||||
|
if ( Puzzle.isEmpty( south )
|
||||||
|
&& shortest > routeFinder.stepsAway(xy.up()) )
|
||||||
|
{
|
||||||
|
shortest = routeFinder.stepsAway(xy.up());
|
||||||
|
pref.set(xy.up());
|
||||||
|
}
|
||||||
|
if ( Puzzle.isEmpty( west )
|
||||||
|
&& shortest > routeFinder.stepsAway(xy.left()) )
|
||||||
|
{
|
||||||
|
shortest = routeFinder.stepsAway(xy.left());
|
||||||
|
pref.set(xy.left());
|
||||||
|
}
|
||||||
|
if ( Puzzle.isEmpty( east )
|
||||||
|
&& shortest > routeFinder.stepsAway( xy.right()) )
|
||||||
|
{
|
||||||
|
shortest = routeFinder.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)
|
||||||
|
|| !tryMoveWorker(animator, pref) )
|
||||||
|
return false;
|
||||||
|
//
|
||||||
|
// Select the box.
|
||||||
|
//
|
||||||
|
animator.select(xy);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Done
|
||||||
|
//
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
176
app/src/main/java/org/vostan/banvor/model/PuzzleRouteFinder.java
Normal file
176
app/src/main/java/org/vostan/banvor/model/PuzzleRouteFinder.java
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package org.vostan.banvor.model;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
public class PuzzleRouteFinder {
|
||||||
|
protected Puzzle puzzle = null;
|
||||||
|
protected int [] moves;
|
||||||
|
protected Vector<XYPair> setOfCells = new Vector<XYPair>();
|
||||||
|
|
||||||
|
public PuzzleRouteFinder(Puzzle p) {
|
||||||
|
puzzle = p;
|
||||||
|
moves = new int[puzzle.getColumnCount() * puzzle.getRowCount()];
|
||||||
|
setOfCells.ensureCapacity(puzzle.getColumnCount() * puzzle.getRowCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final int getMoves( XYPair xy )
|
||||||
|
{
|
||||||
|
return moves[puzzle.getIndex(xy)];
|
||||||
|
}
|
||||||
|
protected void setMoves( XYPair xy, int v )
|
||||||
|
{
|
||||||
|
moves[puzzle.getIndex(xy)]=v;
|
||||||
|
}
|
||||||
|
private boolean setMovesIfGreater( XYPair xy, int l )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// If out of borders then nothing to do.
|
||||||
|
//
|
||||||
|
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(xy) > l && puzzle.isEmpty(puzzle.getSym(xy)))
|
||||||
|
{
|
||||||
|
setMoves(xy,l);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void calcMoves()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Erase moves array.
|
||||||
|
//
|
||||||
|
Arrays.fill(moves,Integer.MAX_VALUE);
|
||||||
|
//
|
||||||
|
// For the beginning there is no cell in the list.
|
||||||
|
//
|
||||||
|
int front = 0;
|
||||||
|
setOfCells.removeAllElements();
|
||||||
|
//
|
||||||
|
// Set the seed.
|
||||||
|
//
|
||||||
|
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 < setOfCells.size() )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Pop the cell.
|
||||||
|
//
|
||||||
|
XYPair xy = setOfCells.elementAt(front++);
|
||||||
|
//
|
||||||
|
// Increase the length of cells all around given cell and push
|
||||||
|
// them into the list.
|
||||||
|
//
|
||||||
|
int l = getMoves(xy)+1;
|
||||||
|
if ( setMovesIfGreater(xy.left(),l) ) {
|
||||||
|
setOfCells.add(xy.left());
|
||||||
|
}
|
||||||
|
if ( setMovesIfGreater(xy.right(),l) ) {
|
||||||
|
setOfCells.add(xy.right());
|
||||||
|
}
|
||||||
|
if ( setMovesIfGreater(xy.down(),l) ) {
|
||||||
|
setOfCells.add(xy.down());
|
||||||
|
}
|
||||||
|
if ( setMovesIfGreater(xy.up(),l) ) {
|
||||||
|
setOfCells.add(xy.up());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int stepsAway( XYPair xy )
|
||||||
|
{
|
||||||
|
return getMoves(xy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isAccessible( XYPair xy )
|
||||||
|
{
|
||||||
|
return puzzle.isValid(xy) && stepsAway(xy) != Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector<XYPair> getDirections( XYPair xy )
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
Vector<XYPair> steps = new Vector<XYPair>(away);
|
||||||
|
steps.add(xy);
|
||||||
|
while ( steps.size() < away )
|
||||||
|
{
|
||||||
|
xy = steps.lastElement();
|
||||||
|
int j = stepsAway(xy);
|
||||||
|
if ( stepsAway(xy.left()) < j ) {
|
||||||
|
steps.add(xy.left());
|
||||||
|
}
|
||||||
|
else if ( stepsAway(xy.right()) < j ) {
|
||||||
|
steps.add(xy.right());
|
||||||
|
}
|
||||||
|
else if ( stepsAway(xy.down()) < j ) {
|
||||||
|
steps.add(xy.down());
|
||||||
|
}
|
||||||
|
else if ( stepsAway(xy.up()) < j ) {
|
||||||
|
steps.add(xy.up());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XYPair [] getPushDirections( XYPair xy )
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// The selected box can be moved only orthogonally.
|
||||||
|
// Check that worker is on opposite side of the box.
|
||||||
|
// Statement:
|
||||||
|
// If scaliar product of selected->x,y and worker->selected is equal
|
||||||
|
// to manhatten distance of x,y from the selected box then the worker
|
||||||
|
// push direction is directed to x,y and is not opposit. In other words
|
||||||
|
// selected->x,y and worker->selected are codirectional.
|
||||||
|
//
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
XYPair ixy = new XYPair(puzzle.getSelected());
|
||||||
|
while ( !xy.isEqual(ixy) )
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
XYPair steps[] = new XYPair[len];
|
||||||
|
int i = 0;
|
||||||
|
ixy.set(puzzle.getSelected());
|
||||||
|
while ( !xy.isEqual(ixy) )
|
||||||
|
{
|
||||||
|
steps[i++] = ixy;
|
||||||
|
ixy = ixy.add(dir);
|
||||||
|
}
|
||||||
|
return steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,7 +2,8 @@ package org.vostan.banvor.model;
|
|||||||
|
|
||||||
import static java.lang.Math.abs;
|
import static java.lang.Math.abs;
|
||||||
|
|
||||||
public class XYPair {
|
public class XYPair
|
||||||
|
{
|
||||||
public static final XYPair UP = new XYPair(0, 1);
|
public static final XYPair UP = new XYPair(0, 1);
|
||||||
public static final XYPair DOWN = 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 LEFT = new XYPair(-1, 0);
|
||||||
@@ -11,6 +12,11 @@ public class XYPair {
|
|||||||
private int _x = 0;
|
private int _x = 0;
|
||||||
private int _y = 0;
|
private int _y = 0;
|
||||||
|
|
||||||
|
public XYPair(){
|
||||||
|
_x = 0;
|
||||||
|
_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
public XYPair(int x, int y){
|
public XYPair(int x, int y){
|
||||||
_x = x;
|
_x = x;
|
||||||
_y = y;
|
_y = y;
|
||||||
@@ -29,6 +35,11 @@ public class XYPair {
|
|||||||
return _y;
|
return _y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void set(int x, int y){
|
||||||
|
_x = x;
|
||||||
|
_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
public void set(XYPair xy){
|
public void set(XYPair xy){
|
||||||
_x = xy.x();
|
_x = xy.x();
|
||||||
_y = xy.y();
|
_y = xy.y();
|
||||||
|
|||||||
Reference in New Issue
Block a user