From 78c58c8ef67bce97171b3f38c7287e736fdd5f7e Mon Sep 17 00:00:00 2001 From: Vahagn Khachatryan Date: Wed, 5 Jun 2013 13:39:52 +0400 Subject: [PATCH] A new short-cut is added for the box continuing pushing with the worker adjusting its position. --- .../vahagn/sokoban/play/PuzzleLogic.java | 385 +++++++++++++----- 1 file changed, 272 insertions(+), 113 deletions(-) diff --git a/src/org/dyndns/vahagn/sokoban/play/PuzzleLogic.java b/src/org/dyndns/vahagn/sokoban/play/PuzzleLogic.java index bf2f334..8c7461a 100644 --- a/src/org/dyndns/vahagn/sokoban/play/PuzzleLogic.java +++ b/src/org/dyndns/vahagn/sokoban/play/PuzzleLogic.java @@ -44,145 +44,304 @@ public class PuzzleLogic if ( Puzzle.isEmpty(tile) ) { // - // Check if worker selected a box and can push it? - // - if ( puzzle.isSelected() ) - { - // - // Get directions to push and act accordingly. - // - int pushes[] = getPushDirections(x, y); - if ( pushes != null ) - { - for ( int i = 0; i < pushes.length; i+=2 ) - animator.queue( new Animator.Push(pushes[i],pushes[i+1]) ); - return true; - } - } - // - // Either nothing was selected or the box cannot be moved to - // tapped location. Try move worker alone. + // Calculate possible moves map. // calcMoves(); - if ( isAccessible(x,y) ) - { - // - // First unselect box. - // - if ( puzzle.isSelected() ) - animator.queue( new Animator.Unselect() ); - // - // Get directions and queue moves accordingly. - // - int dirs[] = getDirections(x, y); - for ( int i = 0; i < dirs.length; i+=2 ) - animator.queue( new Animator.Move(dirs[i],dirs[i+1]) ); - } - else - { - // - // Show that action is not allowed. - // - animator.queue( new Animator.NoMove(x,y) ); - } + // + // Check if worker selected a box and can push it to the location? + // If yes then we are done. + // + if ( puzzle.isSelected() + && tryMoveBox(animator, x, y) ) + return true; + // + // Either nothing was selected or the box cannot be moved to + // tapped location. Try move worker alone. + // + if ( tryMoveWorker( animator, x, y ) ) + return true; + // + // Show that action is not allowed. + // + undoable( animator, x, y ); } // - // The tapped is the worker. Unselect if selected. + // The tapped is the worker. Try move the box. If not possible + // then unselect if selected. // else if ( Puzzle.hasWorker(tile) ) { - if ( puzzle.isSelected() ) + if ( !puzzle.isSelected() ) { - animator.queue( new Animator.Unselect() ); + 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, x, y) ) + 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(); - int north = puzzle.getSym(x,y-1); - int south = puzzle.getSym(x,y+1); - int west = puzzle.getSym(x-1,y); - int east = puzzle.getSym(x+1,y); // - // First check if there is a worker in a nighbour cell. If - // yes then simplly select the box. + // If the box is selected then unselect it. // - if ( Puzzle.hasWorker( west ) - || Puzzle.hasWorker( east ) - || Puzzle.hasWorker( north ) - || Puzzle.hasWorker( south ) ) + if ( puzzle.isSelected(x,y) ) { - if ( !puzzle.isSelected(x,y) ) - { - if ( puzzle.isSelected() ) - animator.queue( new Animator.Unselect() ); - animator.queue( new Animator.Select(x,y) ); - } + unselect( animator ); + return true; } // - // Otherwise, check which is of the cells is in closes walking - // distance and move worker to that cell, then select. + // Try move the worker next to the box and select it if the + // box is not selected yet. // - else - { - int pref_x = -1; - int pref_y = -1; - int shortest = Integer.MAX_VALUE; - if ( Puzzle.isEmpty( north ) - && shortest > stepsAway( x, y-1) ) - { - shortest = stepsAway( x, y-1); - pref_x = x; - pref_y = y-1; - } - if ( Puzzle.isEmpty( south ) - && shortest > stepsAway( x, y+1) ) - { - shortest = stepsAway( x, y+1); - pref_x = x; - pref_y = y+1; - } - if ( Puzzle.isEmpty( west ) - && shortest > stepsAway( x-1, y) ) - { - shortest = stepsAway( x-1, y); - pref_x = x-1; - pref_y = y; - } - if ( Puzzle.isEmpty( east ) - && shortest > stepsAway( x+1, y) ) - { - shortest = stepsAway( x+1, y); - pref_x = x+1; - pref_y = y; - } - // - // Looks there is place to approach to box. - // - if ( shortest < Integer.MAX_VALUE ) - { - if ( puzzle.isSelected() ) - animator.queue( new Animator.Unselect() ); - int dirs[] = getDirections(pref_x, pref_y); - for ( int i = 0; i < dirs.length; i+=2 ) - animator.queue( new Animator.Move(dirs[i],dirs[i+1]) ); - //animator.queue( new Animator.Select(x,y) ); - } - else - { - // - // Show that action is not allowed. - // - animator.queue( new Animator.NoMove(x,y) ); - } - } + if ( trySelectBox( animator, x, y ) ) + return true; + // + // Show that action is not allowed if reached till here. + // + undoable( animator, x, y ); } return true; } + + ////////////////////////////////////////////////////////////////////////// + // + // Routes to accessible cells from where worker stands. + // + protected boolean tryMoveWorker( Animator animator, int x, int y ) + { + // + // If the filed is not accessable then move failed. + // + if ( !isAccessible(x,y) ) + return false; + // + // First unselect box. + // + if ( puzzle.isSelected() ) + unselect( animator ); + // + // Get directions and queue moves accordingly. + // + int dirs[] = getDirections(x, y); + for ( int i = 0; i < dirs.length; i+=2 ) + move( animator, dirs[i],dirs[i+1] ); + // + // Done. + // + return true; + } + protected boolean tryMoveBox( Animator animator, int x, int y ) + { + // + // 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. + // + int bx = puzzle.getSelectedX(); + int by = puzzle.getSelectedY(); + int dx = x-bx; + int dy = y-by; + if ( dx != 0 && dy != 0 ) + return false; + // + // There is no point to continue also in case if the asked cell + // is the box. + // + if ( dx == 0 && dy == 0 ) + return false; + // + // Now find the desired place for the worker to start push this + // box. + // + int wx = bx; + int wy = by; + if ( x < bx ) + ++wx; + else if ( x > bx ) + --wx; + else if ( y < by ) + ++wy; + else + --wy; + // + // Check if the desired place for the worker is accessable? If not + // then there is no point to continue. + // + if ( !isAccessible(wx, wy) ) + return false; + // + // Now check that all cell till x,y are empty and that we can + // push box till there. + // + int sx = bx-wx; + int sy = by-wy; + int ix = bx; + int iy = by; + while ( x != ix || y != iy ) + { + ix+=sx; + iy+=sy; + int v = puzzle.getSym(ix,iy); + 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 ( wx != puzzle.getWorkerX() || wy != puzzle.getWorkerY() ) + { + tryMoveWorker( animator, wx, wy ); + select( animator, bx, by ); + } + // + // Now create the steps to push the box. + // + ix = bx; + iy = by; + while ( x != ix || y != iy ) + { + push( animator, ix, iy ); + ix+=sx; + iy+=sy; + } + // + // Done + // + return true; + } + + protected boolean trySelectBox( Animator animator, int x, int y ) + { + int north = puzzle.getSym(x,y-1); + int south = puzzle.getSym(x,y+1); + int west = puzzle.getSym(x-1,y); + int east = puzzle.getSym(x+1,y); + // + // 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(x,y) ) + { + if ( puzzle.isSelected() ) + unselect( animator ); + select( animator, x, y ); + } + } + // + // Otherwise, check which is of the cells is in closes walking + // distance and move worker to that cell, then select. + // + else + { + int pref_x = -1; + int pref_y = -1; + int shortest = Integer.MAX_VALUE; + if ( Puzzle.isEmpty( north ) + && shortest > stepsAway( x, y-1) ) + { + shortest = stepsAway( x, y-1); + pref_x = x; + pref_y = y-1; + } + if ( Puzzle.isEmpty( south ) + && shortest > stepsAway( x, y+1) ) + { + shortest = stepsAway( x, y+1); + pref_x = x; + pref_y = y+1; + } + if ( Puzzle.isEmpty( west ) + && shortest > stepsAway( x-1, y) ) + { + shortest = stepsAway( x-1, y); + pref_x = x-1; + pref_y = y; + } + if ( Puzzle.isEmpty( east ) + && shortest > stepsAway( x+1, y) ) + { + shortest = stepsAway( x+1, y); + pref_x = x+1; + pref_y = y; + } + // + // Move the worker to the direction. If we cannot move worker + // next to the box then we cannot select it. + // + if ( !tryMoveWorker(animator, pref_x, pref_y) ) + return false; + // + // Select the box. + // + select(animator,x,y); + } + // + // Done + // + return true; + } + + protected void move( Animator animator, int x, int y ) + { + animator.queue( new Animator.Move(x,y) ); + } + + protected void push( Animator animator, int x, int y ) + { + animator.queue( new Animator.Push(x,y) ); + } + + protected void select( Animator animator, int x, int y ) + { + animator.queue( new Animator.Select(x,y) ); + } + + 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, int x, int y ) + { + animator.queue( new Animator.NoMove(x,y) ); + } ////////////////////////////////////////////////////////////////////////// //