Add puzzle solver for monkey madness 1 puzzle box (#6545)

* Add puzzle solver for monkey madness 1 puzzle box
This commit is contained in:
steffenhauge
2019-01-04 21:44:00 +01:00
committed by Lotto
parent 1af2dac241
commit 72cce77784
6 changed files with 940 additions and 25 deletions

View File

@@ -49,6 +49,10 @@ public enum InventoryID
* Barrows reward chest inventory.
*/
BARROWS_REWARD(141),
/**
* Monkey madness puzzle box inventory.
*/
MONKEY_MADNESS_PUZZLE_BOX(221),
/**
* Chambers of Xeric chest inventory.
*/

View File

@@ -52,6 +52,7 @@ import net.runelite.client.plugins.puzzlesolver.solver.PuzzleSolver;
import net.runelite.client.plugins.puzzlesolver.solver.PuzzleState;
import net.runelite.client.plugins.puzzlesolver.solver.heuristics.ManhattanDistance;
import net.runelite.client.plugins.puzzlesolver.solver.pathfinding.IDAStar;
import net.runelite.client.plugins.puzzlesolver.solver.pathfinding.IDAStarMM;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
@@ -105,11 +106,18 @@ public class PuzzleSolverOverlay extends Overlay
return null;
}
boolean useNormalSolver = true;
ItemContainer container = client.getItemContainer(InventoryID.PUZZLE_BOX);
if (container == null)
{
return null;
useNormalSolver = false;
container = client.getItemContainer(InventoryID.MONKEY_MADNESS_PUZZLE_BOX);
if (container == null)
{
return null;
}
}
Widget puzzleBox = client.getWidget(WidgetInfo.PUZZLE_BOX);
@@ -123,7 +131,7 @@ public class PuzzleSolverOverlay extends Overlay
String infoString = "Solving..";
int[] itemIds = getItemIds(container);
int[] itemIds = getItemIds(container, useNormalSolver);
boolean shouldCache = false;
if (solver != null)
@@ -335,7 +343,7 @@ public class PuzzleSolverOverlay extends Overlay
if (solver == null || cachedItems == null
|| (!shouldCache && solver.hasExceededWaitDuration() && !Arrays.equals(cachedItems, itemIds)))
{
solve(itemIds);
solve(itemIds, useNormalSolver);
shouldCache = true;
}
@@ -347,7 +355,7 @@ public class PuzzleSolverOverlay extends Overlay
return null;
}
private int[] getItemIds(ItemContainer container)
private int[] getItemIds(ItemContainer container, boolean useNormalSolver)
{
int[] itemIds = new int[DIMENSION * DIMENSION];
@@ -364,13 +372,10 @@ public class PuzzleSolverOverlay extends Overlay
itemIds[items.length] = BLANK_TILE_VALUE;
}
return convertToSolverFormat(itemIds);
return convertToSolverFormat(itemIds, useNormalSolver);
}
/**
* This depends on there being no gaps in between item ids in puzzles.
*/
private int[] convertToSolverFormat(int[] items)
private int[] convertToSolverFormat(int[] items, boolean useNormalSolver)
{
int lowestId = Integer.MAX_VALUE;
@@ -393,7 +398,15 @@ public class PuzzleSolverOverlay extends Overlay
{
if (items[i] != BLANK_TILE_VALUE)
{
convertedItems[i] = items[i] - lowestId;
int value = items[i] - lowestId;
// The MM puzzle has gaps
if (!useNormalSolver)
{
value /= 2;
}
convertedItems[i] = value;
}
else
{
@@ -410,7 +423,7 @@ public class PuzzleSolverOverlay extends Overlay
System.arraycopy(items, 0, cachedItems, 0, cachedItems.length);
}
private void solve(int[] items)
private void solve(int[] items, boolean useNormalSolver)
{
if (solverFuture != null)
{
@@ -419,7 +432,15 @@ public class PuzzleSolverOverlay extends Overlay
PuzzleState puzzleState = new PuzzleState(items);
solver = new PuzzleSolver(new IDAStar(new ManhattanDistance()), puzzleState);
if (useNormalSolver)
{
solver = new PuzzleSolver(new IDAStar(new ManhattanDistance()), puzzleState);
}
else
{
solver = new PuzzleSolver(new IDAStarMM(new ManhattanDistance()), puzzleState);
}
solverFuture = executorService.submit(solver);
}

View File

@@ -53,20 +53,8 @@ public class PuzzleState
throw new IllegalStateException("Piece array does not have the right dimensions");
}
for (int i = 0; i < pieces.length; i++)
{
if (pieces[i] == BLANK_TILE_VALUE)
{
emptyPiece = i;
}
}
if (emptyPiece == -1)
{
throw new IllegalStateException("Incorrect empty piece passed in!");
}
this.pieces = pieces;
findEmptyPiece();
}
private PuzzleState(PuzzleState state)
@@ -75,6 +63,19 @@ public class PuzzleState
this.emptyPiece = state.emptyPiece;
}
private void findEmptyPiece()
{
for (int i = 0; i < pieces.length; i++)
{
if (pieces[i] == BLANK_TILE_VALUE)
{
this.emptyPiece = i;
return;
}
}
throw new IllegalStateException("Incorrect empty piece passed in!");
}
public List<PuzzleState> computeMoves()
{
List<PuzzleState> moves = new ArrayList<>();
@@ -179,4 +180,46 @@ public class PuzzleState
return h;
}
public PuzzleState swap(int x1, int y1, int x2, int y2)
{
int val1 = getPiece(x1, y1);
int val2 = getPiece(x2, y2);
if (!isValidSwap(x1, y1, x2, y2))
{
throw new IllegalStateException(String.format("Invalid swap: (%1$d, %2$d), (%3$d, %4$d)", x1, y1, x2, y2));
}
PuzzleState newState = new PuzzleState(this);
newState.pieces[y1 * DIMENSION + x1] = val2;
newState.pieces[y2 * DIMENSION + x2] = val1;
newState.findEmptyPiece();
return newState;
}
private boolean isValidSwap(int x1, int y1, int x2, int y2)
{
int absX = Math.abs(x1 - x2);
int absY = Math.abs(y1 - y2);
if (getPiece(x1, y1) != BLANK_TILE_VALUE && getPiece(x2, y2) != BLANK_TILE_VALUE)
{
return false;
}
if (x1 == x2 && absY == 1)
{
return true;
}
if (y1 == y2 && absX == 1)
{
return true;
}
return false;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2018, Steffen Hauge <steffen.oerum.hauge@hotmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.puzzlesolver.solver;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter
public enum PuzzleSwapPattern
{
ROTATE_LEFT_UP(new int[]{1, -1, 0, -1, -1, -1, -1, 0}, 1, 1), //Reference point
ROTATE_LEFT_DOWN(1, -1),
ROTATE_RIGHT_UP(-1, 1),
ROTATE_RIGHT_DOWN(-1, -1),
ROTATE_UP_LEFT(new int[]{-1, 1, -1, 0, -1, -1, 0, -1}, 1 , 1), //Reference point
ROTATE_UP_RIGHT(-1, 1),
ROTATE_DOWN_LEFT(1, -1),
ROTATE_DOWN_RIGHT(-1, -1),
LAST_PIECE_ROW(new int[]{-1, -1, 0, -1, -1, 0, -1, 1}, 1, 1),
LAST_PIECE_COLUMN(new int[]{-1, -1, -1, 0, 0, -1, 1, -1}, 1, 1),
SHUFFLE_UP_RIGHT(new int[]{1, -1, 0, -1}, 1, 1),
SHUFFLE_UP_LEFT(new int[]{-1, -1, 0, -1}, 1, 1),
SHUFFLE_UP_BELOW(new int[]{-1, 1, -1, 0}, 1, 1),
SHUFFLE_UP_ABOVE(new int[]{-1, -1, -1, 0}, 1, 1);
/**
* Points used for swaps relative to locVal
*/
private final int[] points;
/**
* Modifier for X coordinate
*/
private final int modX;
/**
* Modifier for Y coordinate
*/
private final int modY;
PuzzleSwapPattern(int modX, int modY)
{
this(null, modX, modY);
}
}

View File

@@ -0,0 +1,716 @@
/*
* Copyright (c) 2018, Steffen Hauge <steffen.oerum.hauge@hotmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.puzzlesolver.solver.pathfinding;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.runelite.api.Point;
import static net.runelite.client.plugins.puzzlesolver.solver.PuzzleSolver.BLANK_TILE_VALUE;
import static net.runelite.client.plugins.puzzlesolver.solver.PuzzleSolver.DIMENSION;
import net.runelite.client.plugins.puzzlesolver.solver.PuzzleState;
import net.runelite.client.plugins.puzzlesolver.solver.PuzzleSwapPattern;
import static net.runelite.client.plugins.puzzlesolver.solver.PuzzleSwapPattern.*;
import net.runelite.client.plugins.puzzlesolver.solver.heuristics.Heuristic;
public class IDAStarMM extends IDAStar
{
private PuzzleState currentState;
private List<PuzzleState> stateList = new ArrayList<>();
private List<List<Integer>> validRowNumbers = new ArrayList<>();
private List<List<Integer>> validColumnNumbers = new ArrayList<>();
public IDAStarMM(Heuristic heuristic)
{
super(heuristic);
//Add valid numbers for rows and columns
validRowNumbers.add(Arrays.asList(0, 1, 2, 3, 4));
validRowNumbers.add(Arrays.asList(6, 7, 8, 9));
validColumnNumbers.add(Arrays.asList(5, 10, 15, 20));
}
@Override
public List<PuzzleState> computePath(PuzzleState root)
{
currentState = root;
stateList.add(root);
List<PuzzleState> path = new ArrayList<>();
//Reduce to 4x5
solveRow(0);
//Reduce to 4x4
solveColumn();
//Reduce to 3x4
solveRow(1);
//Remove last state
stateList.remove(stateList.size() - 1);
//Pathfinder for 4x4
path.addAll(super.computePath(currentState));
path.addAll(0, stateList);
return path;
}
private void solveRow(int row)
{
for (int i = row; i < DIMENSION; i++)
{
int valTarget = row * DIMENSION + i;
int valCurrent = currentState.getPiece(i, row);
if (valCurrent != valTarget)
{
moveTowardsVal(valTarget, i, row, true);
}
}
}
private void solveColumn()
{
int column = 0;
for (int i = column + 1; i < DIMENSION; i++)
{
int valTarget = column + i * DIMENSION;
int valCurrent = currentState.getPiece(column, i);
if (valCurrent != valTarget)
{
moveTowardsVal(valTarget, column, i, false);
}
}
}
private void moveTowardsVal(int valTarget, int x, int y, boolean rowMode)
{
//Not in place
boolean reached = false;
while (currentState.getPiece(x, y) != valTarget)
{
//Find piece location
Point locVal = findPiece(valTarget);
Point locBlank = findPiece(BLANK_TILE_VALUE);
if (reached)
{
//Swap towards locTarget
if (rowMode)
{
alignTargetX(valTarget, x, y);
swapUpRow(valTarget, x, y);
}
else
{
alignTargetY(valTarget, x, y);
swapLeftColumn(valTarget, x, y);
}
}
else
{
int distX = locVal.getX() - locBlank.getX();
int distY = locVal.getY() - locBlank.getY();
int distAbsX = Math.abs(distX);
int distAbsY = Math.abs(distY);
if (distX == 0)
{
//Same column
if (distAbsY == 1)
{
//Next to
reached = true;
}
else
{
//More than 2 away, move towards on Y-axis
if (distY >= 2)
{
Point locSwap = new Point(locBlank.getX(), locBlank.getY() + 1);
swap(locBlank, locSwap);
}
else if (distY <= -2)
{
Point locSwap = new Point(locBlank.getX(), locBlank.getY() - 1);
swap(locBlank, locSwap);
}
}
}
else if (distY == 0)
{
//Same row
if (distAbsX == 1)
{
//Next to
reached = true;
}
else
{
//More than 2 away, move towards on X-axis
if (distX >= 2)
{
Point locSwap = new Point(locBlank.getX() + 1, locBlank.getY());
swap(locBlank, locSwap);
}
else if (distX <= -2)
{
Point locSwap = new Point(locBlank.getX() - 1, locBlank.getY());
swap(locBlank, locSwap);
}
}
}
else
{
//Different row and column
if (rowMode)
{
//Check if already correct above
if (locBlank.getY() - 1 == y
&& validRowNumbers.get(y).contains(currentState.getPiece(locBlank.getX(), locBlank.getY() - 1))
&& currentState.getPiece(locBlank.getX(), locBlank.getY() - 1) < valTarget
&& distY <= -1)
{
//Move forward
Point locSwap = new Point(locBlank.getX() + 1, locBlank.getY());
swap(locBlank, locSwap);
continue;
}
//Move downwards or upwards
if (distY >= 1)
{
Point locSwap = new Point(locBlank.getX(), locBlank.getY() + 1);
swap(locBlank, locSwap);
}
else if (distY <= -1)
{
Point locSwap = new Point(locBlank.getX(), locBlank.getY() - 1);
swap(locBlank, locSwap);
}
}
else
{
//Check if already correct to the left
if (locBlank.getX() - 1 == x
&& validColumnNumbers.get(x).contains(currentState.getPiece(locBlank.getX() - 1, locBlank.getY()))
&& currentState.getPiece(locBlank.getX() - 1, locBlank.getY()) < valTarget
&& distX <= -1)
{
//Move down
Point locSwap = new Point(locBlank.getX(), locBlank.getY() + 1);
swap(locBlank, locSwap);
continue;
}
//Move right or left
if (distX >= 1)
{
Point locSwap = new Point(locBlank.getX() + 1, locBlank.getY());
swap(locBlank, locSwap);
}
else if (distX <= -1)
{
Point locSwap = new Point(locBlank.getX() - 1, locBlank.getY());
swap(locBlank, locSwap);
}
}
}
}
}
}
private void alignTargetX(int valTarget, int x, int y)
{
Point locVal = findPiece(valTarget);
//Check if same column
if (locVal.getX() == x)
{
return;
}
//1 = right, -1 = left
int direction = Integer.signum(x - locVal.getX());
while (locVal.getX() != x)
{
locVal = findPiece(valTarget);
Point locBlank = findPiece(BLANK_TILE_VALUE);
//Check if aligned
if (x - locVal.getX() == 0)
{
break;
}
if (locVal.getX() == locBlank.getX())
{
int diff = locBlank.getY() - locVal.getY();
if (diff == 1)
{
//Below
Point loc1 = new Point(locBlank.getX() + direction, locBlank.getY());
Point loc2 = new Point(loc1.getX(), loc1.getY() - 1);
swap(locBlank, loc1);
swap(loc1, loc2);
swap(loc2, locVal);
}
else if (diff == -1)
{
//Above
swap(locBlank, locVal);
}
}
else if (locVal.getY() == locBlank.getY())
{
int diff = locBlank.getX() - locVal.getX();
if (diff == 1)
{
//Right
if (direction == 1)
{
swap(locVal, locBlank);
}
else if (direction == -1)
{
//Check space
if (locVal.getY() == DIMENSION - 1)
{
//No space below, use upper rotate
performSwapPattern(locBlank, locVal, ROTATE_LEFT_UP);
}
else
{
//Space below, use lower rotate
performSwapPattern(locBlank, locVal, ROTATE_LEFT_DOWN);
}
}
}
else if (diff == -1)
{
//Left
if (direction == -1)
{
swap(locVal, locBlank);
}
else if (direction == 1)
{
//Check space
if (locVal.getY() == DIMENSION - 1)
{
//No space below, use upper rotate
performSwapPattern(locBlank, locVal, ROTATE_RIGHT_UP);
}
else
{
//Space below, use lower rotate
performSwapPattern(locBlank, locVal, ROTATE_RIGHT_DOWN);
}
}
}
}
}
}
//Swaps up until inserted into the correct place
private void swapUpRow(int valTarget, int x, int y)
{
Point locVal = findPiece(valTarget);
Point locBlank = findPiece(BLANK_TILE_VALUE);
//Check if already placed correct
if (locVal.getX() == x && locVal.getY() == y)
{
return;
}
//Check if simple swap is enough
if (locBlank.getX() == x && locBlank.getY() == y && locVal.getY() - 1 == y)
{
swap(locBlank, locVal);
return;
}
//Move up
while (true)
{
locVal = findPiece(valTarget);
locBlank = findPiece(BLANK_TILE_VALUE);
//Check if already placed correct
if (locVal.getX() == x && locVal.getY() == y)
{
return;
}
if (locVal.getX() == locBlank.getX())
{
int diff = locBlank.getY() - locVal.getY();
if (diff == 1)
{
//Below
//Last piece
if (x == DIMENSION - 1)
{
performSwapPattern(locBlank, locVal, LAST_PIECE_ROW);
return;
}
performSwapPattern(locBlank, locVal, ROTATE_UP_RIGHT);
}
else if (diff == -1)
{
//Above
swap(locBlank, locVal);
}
}
else if (locVal.getY() == locBlank.getY())
{
int diff = locBlank.getX() - locVal.getX();
if (diff == 1)
{
//Right
performSwapPattern(locBlank, locVal, SHUFFLE_UP_RIGHT);
}
else if (diff == -1)
{
//Left
//Don't remove correct pieces from row
if (locVal.getY() - 1 == y)
{
//Swap blank to below and continue
Point loc1 = new Point(locBlank.getX(), locBlank.getY() + 1);
Point loc2 = new Point(loc1.getX() + 1, loc1.getY());
swap(locBlank, loc1);
swap(loc1, loc2);
continue;
}
performSwapPattern(locBlank, locVal, SHUFFLE_UP_LEFT);
}
}
}
}
private void alignTargetY(int valTarget, int x, int y)
{
Point locVal = findPiece(valTarget);
//Check if same row
if (locVal.getY() == y)
{
return;
}
//1 = down, -1 = up
int direction = Integer.signum(y - locVal.getY());
while (locVal.getY() != y)
{
locVal = findPiece(valTarget);
Point locBlank = findPiece(BLANK_TILE_VALUE);
//Check if aligned
if (y - locVal.getY() == 0)
{
break;
}
if (locVal.getY() == locBlank.getY())
{
int diff = locBlank.getX() - locVal.getX();
if (diff == 1)
{
//Right
Point loc1 = new Point(locBlank.getX(), locBlank.getY() + direction);
Point loc2 = new Point(loc1.getX() - 1, loc1.getY());
swap(locBlank, loc1);
swap(loc1, loc2);
swap(loc2, locVal);
}
else if (diff == -1)
{
//Left
swap(locBlank, locVal);
}
}
else if (locVal.getX() == locBlank.getX())
{
int diff = locBlank.getY() - locVal.getY();
if (diff == 1)
{
//Below
if (direction == 1)
{
swap(locVal, locBlank);
}
else if (direction == -1)
{
//Check space
if (locVal.getX() == DIMENSION - 1)
{
//No space to the right, use left rotate
performSwapPattern(locBlank, locVal, ROTATE_UP_LEFT);
}
else
{
//Space to the right, use right rotate
performSwapPattern(locBlank, locVal, ROTATE_UP_RIGHT);
}
}
}
else if (diff == -1)
{
//Above
if (direction == -1)
{
swap(locVal, locBlank);
}
else if (direction == 1)
{
//Check space
if (locVal.getX() == DIMENSION - 1)
{
//No space to the right, use left rotate
performSwapPattern(locBlank, locVal, ROTATE_DOWN_LEFT);
}
else
{
//Space to the right, use right rotate
performSwapPattern(locBlank, locVal, ROTATE_DOWN_RIGHT);
}
}
}
}
}
}
//Swaps left until inserted into the correct place
private void swapLeftColumn(int valTarget, int x, int y)
{
Point locVal = findPiece(valTarget);
Point locBlank = findPiece(BLANK_TILE_VALUE);
//Check if already placed correct
if (locVal.getX() == x && locVal.getY() == y)
{
return;
}
//Check if simple swap is enough
if (locBlank.getX() == x && locBlank.getY() == y && locVal.getX() - 1 == x)
{
swap(locBlank, locVal);
return;
}
//Move left
while (true)
{
locVal = findPiece(valTarget);
locBlank = findPiece(BLANK_TILE_VALUE);
//Check if already placed correct
if (locVal.getX() == x && locVal.getY() == y)
{
return;
}
if (locVal.getX() == locBlank.getX())
{
int diff = locBlank.getY() - locVal.getY();
if (diff == 1)
{
//Below
performSwapPattern(locBlank, locVal, SHUFFLE_UP_BELOW);
}
else if (diff == -1)
{
//Above
//Don't remove correct pices from row
if (locVal.getX() - 1 == x)
{
//Swap blank to right and continue
Point loc1 = new Point(locBlank.getX() + 1, locBlank.getY());
Point loc2 = new Point(loc1.getX(), loc1.getY() + 1);
swap(locBlank, loc1);
swap(loc1, loc2);
continue;
}
performSwapPattern(locBlank, locVal, SHUFFLE_UP_ABOVE);
}
}
else if (locVal.getY() == locBlank.getY())
{
int diff = locBlank.getX() - locVal.getX();
if (diff == 1)
{
//Right
//Last piece
if (y == DIMENSION - 1)
{
performSwapPattern(locBlank, locVal, LAST_PIECE_COLUMN);
return;
}
performSwapPattern(locBlank, locVal, ROTATE_LEFT_DOWN);
}
else if (diff == -1)
{
//Left
swap(locBlank, locVal);
}
}
}
}
private void swap(Point p1, Point p2)
{
PuzzleState newState = currentState.swap(p1.getX(), p1.getY(), p2.getX(), p2.getY());
currentState = newState;
stateList.add(newState);
}
private Point findPiece(int val)
{
for (int x = 0; x < DIMENSION; x++)
{
for (int y = 0; y < DIMENSION; y++)
{
if (currentState.getPiece(x, y) == val)
{
return new Point(x, y);
}
}
}
// This should never happen
throw new IllegalStateException("Piece wasn't found!");
}
/**
* Assumes locBlank is first point for swap and locVal is last point for swap
*
* swap(locBlank, loc1);
* swap(loc1, loc2);
* swap(loc2, locVal);
*/
private void performSwapPattern(Point locBlank, Point locVal, PuzzleSwapPattern pattern)
{
int[] offsets;
switch (pattern)
{
case ROTATE_LEFT_UP:
case ROTATE_RIGHT_UP:
case ROTATE_RIGHT_DOWN:
case ROTATE_LEFT_DOWN:
offsets = ROTATE_LEFT_UP.getPoints();
break;
case ROTATE_UP_LEFT:
case ROTATE_UP_RIGHT:
case ROTATE_DOWN_LEFT:
case ROTATE_DOWN_RIGHT:
offsets = ROTATE_UP_LEFT.getPoints();
break;
default:
offsets = pattern.getPoints();
}
if (offsets == null || offsets.length % 2 == 1)
{
// This should never happen
throw new IllegalStateException("Unexpected points given in pattern!");
}
int modX = pattern.getModX();
int modY = pattern.getModY();
ArrayList<Point> points = new ArrayList<>();
for (int i = 0; i < offsets.length; i += 2)
{
int x = locVal.getX() + modX * offsets[i];
int y = locVal.getY() + modY * offsets[i + 1];
points.add(new Point(x, y));
}
// Add locVal as last point
points.add(locVal);
if (pattern != LAST_PIECE_ROW && pattern != LAST_PIECE_COLUMN)
{
Point start = locBlank;
for (Point p : points)
{
swap(start, p);
start = p;
}
}
else
{
Point loc1 = points.get(0);
Point loc2 = points.get(1);
Point loc3 = points.get(2);
Point loc4 = points.get(3);
swap(locBlank, locVal);
swap(locVal, loc3);
swap(loc3, loc1);
swap(loc1, loc2);
swap(loc2, locVal);
swap(locVal, loc3);
swap(loc3, loc1);
swap(loc1, loc2);
swap(loc2, locVal);
swap(locVal, locBlank);
swap(locBlank, loc4);
swap(loc4, loc3);
swap(loc3, loc1);
swap(loc1, loc2);
swap(loc2, locVal);
}
}
}

View File

@@ -29,6 +29,7 @@ import net.runelite.client.plugins.puzzlesolver.solver.PuzzleSolver;
import net.runelite.client.plugins.puzzlesolver.solver.PuzzleState;
import net.runelite.client.plugins.puzzlesolver.solver.heuristics.ManhattanDistance;
import net.runelite.client.plugins.puzzlesolver.solver.pathfinding.IDAStar;
import net.runelite.client.plugins.puzzlesolver.solver.pathfinding.IDAStarMM;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -83,8 +84,72 @@ public class PuzzleSolverTest
new PuzzleState(new int[]{1, 5, 2, 3, 4, -1, 0, 7, 14, 8, 11, 6, 13, 9, 23, 10, 12, 15, 19, 17, 20, 21, 16, 22, 18}),
};
private static final PuzzleState[] START_STATES_MM =
{
new PuzzleState(new int[]{0, 5, 1, 3, 4, 15, 2, 8, 10, 9, 22, 16, 11, 6, 14, 7, 21, 23, 12, 18, 20, -1, 17, 13, 19}),
new PuzzleState(new int[]{0, 12, 8, 13, 4, 3, 16, 2, 1, 9, 21, 5, 6, 10, 14, 7, 17, 20, 18, -1, 15, 11, 22, 23, 19}),
new PuzzleState(new int[]{1, 3, 7, 9, 8, 13, 17, 2, 4, 6, 15, 5, 22, 12, 14, 11, 0, 10, 21, -1, 20, 18, 16, 23, 19}),
new PuzzleState(new int[]{5, 2, 16, 14, 4, 3, 0, 8, 9, 11, 15, 6, 17, 19, 7, 1, 10, -1, 23, 18, 20, 12, 21, 13, 22}),
new PuzzleState(new int[]{0, 6, 1, 4, 13, 10, 2, 16, 7, 3, 20, -1, 8, 14, 9, 21, 5, 18, 11, 19, 17, 15, 12, 22, 23}),
new PuzzleState(new int[]{5, 0, 1, 4, 8, 10, 6, 7, 12, 3, 17, 16, 21, 2, 9, 18, 20, 13, 14, 19, 11, -1, 23, 15, 22}),
new PuzzleState(new int[]{1, 9, 2, 13, 17, 5, 7, 8, 3, 22, 6, -1, 16, 12, 4, 15, 18, 0, 23, 14, 10, 21, 11, 20, 19}),
new PuzzleState(new int[]{1, 2, 11, 13, 4, 21, 7, 3, 6, 9, 0, 8, 10, 19, 14, 20, 12, 16, 23, -1, 5, 17, 15, 22, 18}),
new PuzzleState(new int[]{2, 0, 1, 4, 13, 6, 7, 3, 8, 9, 22, 15, 10, 14, 18, 5, 12, -1, 17, 21, 20, 11, 23, 16, 19}),
new PuzzleState(new int[]{0, 1, 2, 8, 3, 6, 12, 22, 9, 7, 11, 21, 13, 4, 14, 5, 10, -1, 18, 19, 20, 15, 16, 23, 17}),
new PuzzleState(new int[]{1, 2, 3, 4, 8, 0, 6, 15, 14, 18, 16, 17, 20, -1, 9, 10, 12, 22, 11, 13, 21, 7, 5, 19, 23}),
new PuzzleState(new int[]{0, 5, 2, 4, 9, 7, 15, 20, 12, 13, 6, -1, 22, 1, 8, 10, 11, 23, 14, 3, 21, 16, 17, 19, 18}),
new PuzzleState(new int[]{0, 1, 9, 6, 13, 5, 18, -1, 4, 2, 15, 12, 3, 17, 7, 16, 10, 8, 23, 14, 20, 21, 19, 11, 22}),
new PuzzleState(new int[]{11, 5, 12, 3, 4, 15, 8, 0, 7, 1, 6, -1, 19, 2, 9, 16, 10, 13, 17, 23, 20, 21, 22, 14, 18}),
new PuzzleState(new int[]{10, 0, 1, 3, 4, 18, 5, 6, 12, 9, 7, 11, 8, -1, 22, 15, 23, 14, 19, 13, 20, 2, 17, 16, 21}),
new PuzzleState(new int[]{19, -1, 6, 2, 4, 0, 21, 10, 3, 9, 1, 15, 17, 8, 14, 11, 13, 22, 7, 18, 16, 12, 5, 23, 20}),
new PuzzleState(new int[]{11, 6, 3, 4, 9, 1, 10, 16, 2, 7, 5, 0, 13, -1, 12, 21, 8, 18, 17, 14, 15, 20, 22, 23, 19}),
new PuzzleState(new int[]{0, 1, 5, 3, 4, -1, 6, 2, 15, 10, 7, 8, 23, 16, 13, 22, 11, 9, 12, 14, 20, 21, 18, 17, 19}),
new PuzzleState(new int[]{10, 0, 1, -1, 2, 6, 5, 4, 13, 9, 16, 17, 12, 8, 19, 20, 15, 7, 21, 11, 22, 18, 14, 23, 3}),
new PuzzleState(new int[]{1, 0, 5, 3, 9, 20, 15, 7, 2, 14, 6, 4, 12, -1, 8, 13, 18, 10, 23, 11, 21, 16, 17, 19, 22}),
new PuzzleState(new int[]{0, 7, 6, 3, 4, 15, 1, 2, 8, 18, 11, 5, 13, -1, 22, 17, 16, 23, 14, 9, 20, 10, 12, 19, 21}),
new PuzzleState(new int[]{5, 7, 0, 2, 9, 10, 1, 11, 3, 4, 16, 22, 8, 14, 17, 15, 20, 12, 13, 6, 21, 23, 19, -1, 18}),
new PuzzleState(new int[]{3, 0, 1, 5, 4, 11, 6, 2, 16, 9, 15, 10, 7, 12, 13, 21, 19, -1, 22, 8, 20, 17, 14, 18, 23}),
new PuzzleState(new int[]{6, 0, 3, 2, 4, 5, 1, 8, 13, 12, 15, 14, 10, 7, 9, -1, 22, 11, 19, 23, 16, 20, 17, 21, 18}),
new PuzzleState(new int[]{11, 5, 6, 8, 9, 0, 21, 16, 4, 3, 17, 18, 2, 7, 1, 15, 10, -1, 22, 14, 20, 19, 13, 12, 23}),
new PuzzleState(new int[]{2, 18, 3, 11, 4, -1, 5, 6, 12, 1, 10, 20, 0, 7, 9, 21, 15, 14, 23, 19, 16, 22, 13, 8, 17}),
new PuzzleState(new int[]{0, 6, 8, 3, 1, 5, 2, 12, 9, 13, 16, 14, 19, 7, 18, 10, 11, -1, 4, 15, 20, 17, 23, 21, 22}),
new PuzzleState(new int[]{1, 16, 10, 4, 3, 0, 15, 2, 9, -1, 8, 5, 23, 12, 6, 21, 18, 14, 13, 11, 20, 22, 7, 19, 17}),
new PuzzleState(new int[]{1, 7, 6, 3, 4, 0, 2, 14, 5, 22, 18, 21, 16, 9, 13, 10, 20, -1, 8, 17, 15, 23, 11, 19, 12}),
new PuzzleState(new int[]{5, 0, 1, 7, 9, 11, 8, 4, 2, 14, 15, 17, 18, -1, 3, 20, 10, 12, 22, 19, 16, 6, 13, 21, 23}),
new PuzzleState(new int[]{5, 0, 6, 14, 7, 13, 15, 1, 3, 10, 20, 9, 17, 4, 2, 11, 12, 8, 19, -1, 21, 16, 22, 18, 23}),
new PuzzleState(new int[]{12, 7, 8, 4, 9, 6, 11, 15, 2, 1, 5, -1, 13, 16, 3, 17, 0, 10, 18, 14, 20, 22, 21, 19, 23}),
new PuzzleState(new int[]{15, 1, 2, 3, 14, -1, 20, 9, 4, 19, 0, 6, 7, 16, 13, 10, 5, 12, 17, 18, 22, 11, 21, 23, 8}),
new PuzzleState(new int[]{0, 1, 17, -1, 14, 6, 4, 2, 3, 16, 10, 18, 13, 19, 9, 7, 5, 8, 21, 22, 11, 20, 15, 12, 23}),
new PuzzleState(new int[]{5, 11, 9, 0, 3, 8, 14, -1, 6, 4, 1, 13, 7, 2, 19, 10, 21, 18, 23, 17, 15, 20, 12, 16, 22}),
new PuzzleState(new int[]{2, 0, 14, -1, 4, 18, 1, 10, 12, 13, 5, 9, 11, 22, 7, 15, 8, 17, 19, 3, 20, 21, 6, 16, 23}),
new PuzzleState(new int[]{0, 1, 13, 9, 2, 6, 8, 22, 3, 4, 12, 16, 10, 7, 19, -1, 5, 11, 14, 17, 15, 20, 21, 18, 23}),
new PuzzleState(new int[]{0, 13, 17, 8, 3, 5, 1, 12, 14, 4, 10, -1, 6, 7, 9, 15, 23, 2, 16, 19, 20, 11, 21, 22, 18}),
new PuzzleState(new int[]{5, 10, 7, 2, 9, 15, 0, -1, 1, 3, 18, 4, 17, 12, 14, 21, 11, 6, 8, 23, 20, 16, 22, 19, 13}),
new PuzzleState(new int[]{0, 3, 1, 2, 4, 10, 5, 7, 8, 9, 11, 6, 21, 13, 12, 20, 17, -1, 14, 19, 22, 18, 15, 16, 23}),
new PuzzleState(new int[]{0, 2, 7, 11, 13, 3, 14, 1, 4, 9, 5, -1, 12, 8, 18, 20, 10, 15, 22, 23, 17, 16, 6, 21, 19}),
new PuzzleState(new int[]{0, 16, 3, 22, 7, 11, 6, -1, 9, 4, 2, 1, 13, 12, 18, 5, 10, 8, 19, 14, 15, 20, 17, 23, 21}),
new PuzzleState(new int[]{0, 13, 5, 12, 3, 2, 10, 4, 6, 8, 1, 21, 19, 14, 9, 17, 23, 22, 16, 11, 15, 7, 20, -1, 18}),
new PuzzleState(new int[]{14, 5, 6, 12, 4, 10, 20, 1, 0, 23, 2, 16, 13, 19, 3, 15, 22, -1, 9, 8, 11, 7, 18, 17, 21}),
new PuzzleState(new int[]{0, 1, 2, 4, 7, 5, 11, -1, 18, 8, 16, 10, 12, 13, 3, 17, 6, 21, 23, 9, 15, 20, 22, 14, 19}),
new PuzzleState(new int[]{1, 6, 7, 3, 4, 5, 17, 0, 22, 12, 10, 15, 8, -1, 14, 11, 13, 16, 18, 19, 20, 2, 21, 9, 23}),
};
private static final int[] FINISHED_STATE = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, -1};
@Test
public void testSolverMM()
{
for (PuzzleState state : START_STATES_MM)
{
PuzzleSolver solver = new PuzzleSolver(new IDAStarMM(new ManhattanDistance()), state);
solver.run();
assertTrue(solver.hasSolution());
assertFalse(solver.hasFailed());
assertTrue(solver.getStep(solver.getStepCount() - 1).hasPieces(FINISHED_STATE));
}
}
@Test
public void testSolver()
{