Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pulled in Chen Shuang's skewb support! There's also more aggressive s…
…q1 filtering in here.
- Loading branch information
Showing
9 changed files
with
714 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,270 @@ | ||
package puzzle; | ||
|
||
import static net.gnehzr.tnoodle.utils.GwtSafeUtils.azzert; | ||
|
||
import net.gnehzr.tnoodle.svglite.Color; | ||
import net.gnehzr.tnoodle.svglite.Svg; | ||
import net.gnehzr.tnoodle.svglite.Dimension; | ||
import net.gnehzr.tnoodle.svglite.Path; | ||
import net.gnehzr.tnoodle.svglite.Transform; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.Random; | ||
import java.util.logging.Logger; | ||
|
||
import puzzle.SkewbSolver.SkewbSolverState; | ||
|
||
import net.gnehzr.tnoodle.scrambles.InvalidScrambleException; | ||
import net.gnehzr.tnoodle.scrambles.Puzzle; | ||
import net.gnehzr.tnoodle.scrambles.PuzzleStateAndGenerator; | ||
import net.gnehzr.tnoodle.utils.GwtSafeUtils; | ||
|
||
import org.timepedia.exporter.client.Export; | ||
|
||
@Export | ||
public class SkewbPuzzle extends Puzzle { | ||
private static final int MIN_SCRAMBLE_LENGTH = 9; | ||
private static final Logger l = Logger.getLogger(SkewbPuzzle.class.getName()); | ||
private SkewbSolver skewbSolver = null; | ||
|
||
private static final int pieceSize = 30; | ||
private static final int gap = 3; | ||
|
||
private static final double sq3d2 = Math.sqrt(3) / 2; | ||
|
||
public SkewbPuzzle() { | ||
skewbSolver = new SkewbSolver(); | ||
wcaMinScrambleDistance = 4; | ||
} | ||
|
||
@Override | ||
public PuzzleStateAndGenerator generateRandomMoves(Random r) { | ||
SkewbSolverState state = skewbSolver.randomState(r); | ||
String scramble = skewbSolver.generateExactly(state, MIN_SCRAMBLE_LENGTH); | ||
|
||
PuzzleState pState; | ||
try { | ||
pState = getSolvedState().applyAlgorithm(scramble); | ||
} catch (InvalidScrambleException e) { | ||
azzert(false, e); | ||
return null; | ||
} | ||
return new PuzzleStateAndGenerator(pState, scramble); | ||
} | ||
|
||
/************************************************************* | ||
* Functions to display the puzzle | ||
*/ | ||
|
||
|
||
private static final HashMap<String, Color> defaultColorScheme = new HashMap<String, Color>(); | ||
static { | ||
defaultColorScheme.put("U", Color.WHITE); | ||
defaultColorScheme.put("R", Color.BLUE); | ||
defaultColorScheme.put("F", Color.RED); | ||
defaultColorScheme.put("D", Color.YELLOW); | ||
defaultColorScheme.put("L", Color.GREEN); | ||
defaultColorScheme.put("B", new Color(0xFF8000)); | ||
} | ||
|
||
@Override | ||
public HashMap<String, Color> getDefaultColorScheme() { | ||
return new HashMap<String, Color>(defaultColorScheme); | ||
} | ||
|
||
private Transform[] getFaceTrans() { | ||
Transform[] position = { | ||
new Transform(pieceSize*sq3d2, -pieceSize/2, pieceSize*sq3d2, pieceSize/2, (pieceSize*4+gap*1.5)*sq3d2, pieceSize), | ||
new Transform(pieceSize*sq3d2, -pieceSize/2, 0, pieceSize, (pieceSize*7+gap*3)*sq3d2, pieceSize * 1.5), | ||
new Transform(pieceSize*sq3d2, -pieceSize/2, 0, pieceSize, (pieceSize*5+gap*2)*sq3d2, pieceSize * 2.5 + 0.5 * gap), | ||
new Transform(0, pieceSize, -pieceSize*sq3d2, -pieceSize/2, (pieceSize*3+gap*1)*sq3d2, pieceSize * 4.5 + 1.5 * gap), | ||
new Transform(pieceSize*sq3d2, pieceSize/2, 0, pieceSize, (pieceSize*3+gap*1)*sq3d2, pieceSize * 2.5 + 0.5 * gap), | ||
new Transform(pieceSize*sq3d2, pieceSize/2, 0, pieceSize, pieceSize*sq3d2, pieceSize * 1.5), | ||
}; | ||
return position; | ||
} | ||
|
||
@Override | ||
public Dimension getPreferredSize() { | ||
return new Dimension( | ||
(int) Math.ceil((3 * gap + 8 * pieceSize + 1) * sq3d2), | ||
(int) Math.ceil(2 * gap + 6 * pieceSize + 1)); | ||
} | ||
|
||
@Override | ||
public String getLongName() { | ||
return "Skewb"; | ||
} | ||
|
||
@Override | ||
public String getShortName() { | ||
return "skewb"; | ||
} | ||
|
||
@Override | ||
public PuzzleState getSolvedState() { | ||
return new SkewbState(); | ||
} | ||
|
||
@Override | ||
protected int getRandomMoveCount() { | ||
return 15; | ||
} | ||
|
||
public class SkewbState extends PuzzleState { | ||
|
||
/** | ||
* +---------+ | ||
* | 1 2 | | ||
* U > | 0-0 | | ||
* | 3 4 | | ||
* +---------+---------+---------+---------+ | ||
* | 1 2 | 1 2 | 1 2 | 1 2 | | ||
* | 4-0 | 2-0 | 1-0 | 5-0 | | ||
* | 3 4 | 3 4 | 3 4 | 3 4 | | ||
* +---------+---------+---------+---------+ | ||
* ^ | 1 2 | | ||
* FL | 3-0 | | ||
* | 3 4 | | ||
* +---------+ | ||
*/ | ||
private int[][] image = new int[6][5]; | ||
|
||
SkewbState() { | ||
for (int i=0; i<6; i++) { | ||
for (int j=0; j<5; j++) { | ||
image[i][j] = i; | ||
} | ||
} | ||
} | ||
|
||
SkewbState(int[][] _image) { | ||
for (int i=0; i<6; i++) { | ||
for (int j=0; j<5; j++) { | ||
image[i][j] = _image[i][j]; | ||
} | ||
} | ||
} | ||
|
||
private void turn(int axis, int pow, int[][] image) { | ||
//axis:0-R 1-U 2-L 3-B | ||
for (int p=0; p<pow; p++) { | ||
switch (axis) { | ||
case 0: | ||
swap(2, 0, 3, 0, 1, 0, image); | ||
swap(2, 4, 3, 2, 1, 3, image); | ||
swap(2, 2, 3, 1, 1, 4, image); | ||
swap(2, 3, 3, 4, 1, 1, image); | ||
swap(4, 4, 5, 3, 0, 4, image); | ||
break; | ||
case 1: | ||
swap(0, 0, 1, 0, 5, 0, image); | ||
swap(0, 2, 1, 2, 5, 1, image); | ||
swap(0, 4, 1, 4, 5, 2, image); | ||
swap(0, 1, 1, 1, 5, 3, image); | ||
swap(4, 1, 2, 2, 3, 4, image); | ||
break; | ||
case 2: | ||
swap(4, 0, 5, 0, 3, 0, image); | ||
swap(4, 3, 5, 4, 3, 3, image); | ||
swap(4, 1, 5, 3, 3, 1, image); | ||
swap(4, 4, 5, 2, 3, 4, image); | ||
swap(2, 3, 0, 1, 1, 4, image); | ||
break; | ||
case 3: | ||
swap(1, 0, 3, 0, 5, 0, image); | ||
swap(1, 4, 3, 4, 5, 3, image); | ||
swap(1, 3, 3, 3, 5, 1, image); | ||
swap(1, 2, 3, 2, 5, 4, image); | ||
swap(0, 2, 2, 4, 4, 3, image); | ||
break; | ||
default: | ||
azzert(false); | ||
} | ||
} | ||
} | ||
|
||
private void swap(int f1, int s1, int f2, int s2, int f3, int s3, int[][] image) { | ||
int temp = image[f1][s1]; | ||
image[f1][s1] = image[f2][s2]; | ||
image[f2][s2] = image[f3][s3]; | ||
image[f3][s3] = temp; | ||
} | ||
|
||
/** | ||
* return a square skewb face. whose 4 corners are (-1, -1), (1, -1), (1, 1), (-1, 1). It will be transformed later. | ||
*/ | ||
private Path[] getFacePaths() { | ||
Path[] p = new Path[5]; | ||
for (int i=0; i<5; i++) { | ||
p[i] = new Path(); | ||
// In svg, by default, borders are scaled along with shapes. | ||
// Setting vector-effect to non-scaling-stroke disables that. | ||
// Unfortunately, batik doesn't support it, so we have | ||
// to do something hacky by explicitly setting the | ||
// stroke-width to something teeny. | ||
// If Batik ever changes to support vector-effect, we | ||
// can clean this up. | ||
//p[i].setAttribute("vector-effect", "non-scaling-stroke"); | ||
p[i].setAttribute("stroke-width", 1.0/pieceSize + "px"); | ||
} | ||
p[0].moveTo(-1, 0); p[0].lineTo( 0, 1); p[0].lineTo( 1, 0); p[0].lineTo(0,-1); p[0].closePath(); | ||
p[1].moveTo(-1, 0); p[1].lineTo(-1,-1); p[1].lineTo( 0,-1); p[1].closePath(); | ||
p[2].moveTo( 0,-1); p[2].lineTo( 1,-1); p[2].lineTo( 1, 0); p[2].closePath(); | ||
p[3].moveTo(-1, 0); p[3].lineTo(-1, 1); p[3].lineTo( 0, 1); p[3].closePath(); | ||
p[4].moveTo( 0, 1); p[4].lineTo( 1, 1); p[4].lineTo( 1, 0); p[4].closePath(); | ||
return p; | ||
} | ||
|
||
protected Svg drawScramble(HashMap<String, Color> colorScheme) { | ||
Svg g = new Svg(getPreferredSize()); | ||
Color[] scheme = new Color[6]; | ||
for(int i = 0; i < scheme.length; i++) { | ||
scheme[i] = colorScheme.get("URFDLB".charAt(i)+""); | ||
} | ||
Transform[] position = getFaceTrans(); | ||
for (int face=0; face<6; face++) { | ||
Path[] p = getFacePaths(); | ||
for (int i=0; i<5; i++) { | ||
p[i].transform(position[face]); | ||
p[i].setFill(scheme[image[face][i]]); | ||
p[i].setStroke(Color.BLACK); | ||
g.appendChild(p[i]); | ||
} | ||
} | ||
return g; | ||
} | ||
|
||
public HashMap<String, PuzzleState> getSuccessors() { | ||
HashMap<String, PuzzleState> successors = new HashMap<String, PuzzleState>(); | ||
String axes = "RULB"; | ||
for(int axis = 0; axis < axes.length(); axis++) { | ||
char face = axes.charAt(axis); | ||
for(int pow = 1; pow <= 2; pow++) { | ||
String turn = "" + face; | ||
if(pow == 2) { | ||
turn += "'"; | ||
} | ||
int[][] imageCopy = new int[image.length][image[0].length]; | ||
GwtSafeUtils.deepCopy(image, imageCopy); | ||
turn(axis, pow, imageCopy); | ||
successors.put(turn, new SkewbState(imageCopy)); | ||
} | ||
} | ||
|
||
return successors; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
// Sure this could blow up with a cast exception, but shouldn't it? =) | ||
return Arrays.deepEquals(image, ((SkewbState) other).image); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Arrays.deepHashCode(image); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.