I'm having an issue and I'm not certain if it's recursion-based. I created a GUI maze that solves itself but the curser jumps over any recursion-traveled square instead of re-traveling the square. Even though it ultimately finds the goal, I want to show it's entire path but I can't stop the curser from jumping around.
I'm using Runnable to track the maze in real-time so I can see it bounce but without the recursion-travel keeping it bound, the recursive functions cease to work (it just bounces back and forth which, again, I don't quite understand.) I started java about three months ago in an accelerated program so I'm not sure if the issue is my understanding of recursion, or a simple addition to a method, or if I'll have to rewrite a large portion of code.
I included the whole code just in case but really it's an issue that's within the travel method or the visited method. Would the answer be to write an entirely new method that re-travels the changed "visited" string maze? I've been struggling with this for a bit and I just need some direction toward an answer.
import java.awt.*;
import javax.swing.*;
class extraCreditMaze extends JPanel implements Runnable { //uses Runnable to execute jPanel
private String [][] ratMaze = //string maze
{{"blocked","blocked","blocked","blocked","blocked","blocked","blocked","blocked"},
{"blocked","open","blocked","blocked","blocked","blocked","blocked","blocked"},
{"blocked","open","open","open","open","open","open","blocked"},
{"blocked","blocked","open","blocked","open","blocked","open","blocked"},
{"blocked","blocked","open","blocked","open","blocked","open","goal"},
{"blocked","open","open","open","blocked","open","open","blocked"},
{"blocked","blocked","blocked","open","open","open","blocked","blocked"},
{"blocked","blocked","blocked","blocked","blocked","blocked","blocked","blocked"}};
final private int SquareSize = 15;
final private int BoardSize = 17;
private boolean free = false;
int axisX = 1, axisY = 1;
public void paintComponent(Graphics color) //paint components for char
{
super.paintComponent(color);
for(int row = 0; row < ratMaze.length; row++)
{
for(int col = 0; col< ratMaze.length; col++)
{
if(row==axisX && col==axisY) //traveling curser = blue
{
color.setColor(Color.blue);
color.fillOval(col*15,row*15,15,15);
}
else if(ratMaze[row][col]=="blocked") //empty = black
{
color.setColor(Color.black);
color.fillRect(col*SquareSize,row*SquareSize,BoardSize,BoardSize);
}
else if(ratMaze[row][col]=="goal")
{
color.setColor(Color.red); //goal = red
color.fillOval(col*15,row*15,15,15);
}
else if(ratMaze[row][col]=="visited")
{
color.setColor(Color.green); //path traveled = green
color.fillOval(col*15,row*15,15,15);
}
else
{
color.setColor(Color.white); //empty space = white
color.fillRect(col*SquareSize,row*SquareSize,BoardSize,BoardSize);
}
}
}
}
public void run () //starts run at (1,1)
{
travel(1,1);
}
public boolean goal(int x, int y){ //method to check goal (true/false)
if(ratMaze[x][y]=="goal")
return true;
else
return false;
}
public void changedVisited(int x, int y) //method to change traveled
{
ratMaze[x][y] = "visited";
axisX = x;
axisY = y;
}
public boolean boundaries(int x, int y) //check boundaries
{
if(ratMaze[x][y]=="blocked")
return true;
else
return false;
}
public boolean visited(int x, int y) //check if visited
{
if(ratMaze[x][y]=="visited")
return true;
else
return false;
}
private void travel(int x, int y)
{
if(boundaries(x,y)) //makes sure it's within bounds
return;
if(visited(x,y)) //makes sure it hasn't already been visited
return;
if(goal(x,y)) //checks if it's the goal/changes boolean
{
free = true;
JOptionPane.showMessageDialog(this, "You did it, Dr. Cui!"); //fun message!
}
if(!free) //all recursion functions if free=false
{
changedVisited(x,y); //changes traveled block to "visited"
repaint(); //repaints visited
try {Thread.sleep(300); } catch (Exception e) { }//slows down the traveling curser
//I do not understand catch (Exception e)
travel(x-1,y); //recursive travel functions
travel(x+1,y);
travel(x,y-1);
travel(x,y+1);
}
}
}
public class runExtraCreditMaze {
public static void main (String [] args) { //JFrame panel and perimeters
JFrame output = new JFrame();
output.setSize(115, 150);
output.setTitle("The Rat Maze");
output.setLocationRelativeTo(null);
extraCreditMaze Maze = new extraCreditMaze();
output.setContentPane(Maze);
output.setVisible(true);
Thread runnable = new Thread(Maze); //Creates Runnable thread for Maze object
runnable.start(); //Starts Runnable thread of Maze object
}
}
Problem is, as you wrote with the "visited". You are missing an decision tree on what to do, when there is no valid move and you are not in the goal. You will need to allow your rat to back track itself. You will probably need to "free" the visited cells when returning from no valid move.
I will try to add some code samples when I get to IDE :)
update: this is very badly written, and it is a bit lagging. but it should work. It needs a bit of cleaning and verification... I reused your boolean variable, which is bad .. :) and switched the true/false. I will do a bit of cleaning up tomorrow just to leave a nicer answer, but I think you will manage to understand what is going on.
update2:I have cleaned it a bit. Important lessons here are as follows:
1) backtracking needs to be done when all 4 steps fails. When your rat have nowhere to go, you need to disqualify the cell from your shortest path (ratMaze[x][y]="open")
2) You need to change position of your rat, when you return from recursion call, but before you continue with next step into. You will also need to let your program know that you are returning from recursion (thus the isBacktracking)
private void repaintMaze(int x, int y) {
changedVisited(x, y); //changes traveled block to "visited"
repaint(); //repaints visited
isBacktracking = false;
try {
Thread.sleep(500);
} catch (Exception e) {
}
}
private boolean travel(int x, int y) {
if (goal(x, y)) //checks if it's the goal/changes boolean
{
JOptionPane.showMessageDialog(this, "You did it, Dr. Cui!");//fun message!
return true;
}
if (boundaries(x, y) || visited(x, y)) //makes sure it's within bounds
return false;
repaintMaze(x, y);
boolean result; //recursive travel functions
result = travel(x - 1, y);
if (result) {
return true;
}
if (isBacktracking) {
repaintMaze(x, y);
}
result = travel(x + 1, y);
if (result) {
return true;
}
if (isBacktracking) {
repaintMaze(x, y);
}
result = travel(x, y - 1);
if (result) {
return true;
}
if (isBacktracking) {
repaintMaze(x, y);
}
result = travel(x, y + 1);
if (result) {
return true;
}
if (isBacktracking) {
repaintMaze(x, y);
}
ratMaze[x][y] = "open";
isBacktracking = true;
return false;
}
you should also add exit on close to your JFrame :) It will stop the application once you click the X button on the window itself
output.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
Related
I wrote code that is supposed to remove 25 HP (health points) each time a rectangle (created by pressing the m key) intersects another rectangle, the enemy. Currently, the enemy looses 25HP continuously after I've pressed the m key only once until I press the m key again outside of the enemy body.
This is my damage code for the enemy. It turns white when killed, that's why I have df1 etc. there.
void damage() {
//if (callMethod) {
HP -=25;
//callMethod = false;
System.out.println(" " + HP);
//}
if (HP == 0) {
df1 = 200;
df2 = 200;
df3 = 200;
}
}
This is the code for the m input.
void Fight() {
if (keyPressed) {
if (key == 'm'|| key == 'M') {
//villkor, flytta höger, X-led.
fill(255, 0, 0, 63);
noStroke();
rect(xF, yF, wF, hF);
xFF = xF;
yFF = yF;
wFF = wF;
hFF = hF;
}
}
}
Here I have my intersect code:
if (g.intersect(f)) {
f.damage();
}
I would appreciate any help I can get. Apologies for my bad English grammar :)
You can use another boolean variable that tracks whether the action was already taken.
Here's a small example:
boolean alreadyPressed = false;
void draw() {}
void mousePressed() {
if(!alreadyPressed){
background(random(255));
}
alreadyPressed = true;
}
You can then reset the boolean variable whenever you want to be eligible to detect the event again.
(I hope this is not a duplicate as the many questions I came into do not fit my need)
I'm developping a 2D grid based game with 2 players with grid. There are two players: blue and red, each one places a stone in cells. So I want to find a path passing throught all cells with the same color back to the starting point, BUT ONLY if there is at least ONE cell that contains opponent's stone.
From the screenshot above: The red stones here in the upper right do not form a valid path. And those in the center are not forming a path neither even though that should be one.
I'm able to find a path but it is somehow broken, it doesn't work as expected.
EDIT:
Pather class
public class Pather {
private static final int MIN_PATH_LENGTH = 3;
public enum Neighbor{
UP_RIGHT(0,1,-1),
RIGHT(1,1,0),
DOWN_RIGHT(2,1,1),
DOWN(3,0,1),
DOWN_LEFT(4,-1,1),
LEFT(5,-1,0),
UP_LEFT(6,-1,-1),
UP(7,0,-1);
public int index, x, y;
Neighbor(int index, int x, int y){
this.index = index;
this.x = x;
this.y = y;
}
}
private static Neighbor[] neighbors = Neighbor.values();
public static ArrayList<Path> findPaths(Stone[][] gameBoard){
ArrayList<Path> paths = new ArrayList<>();
ArrayList<Point> checkedPoints = new ArrayList<>();
for (int i = 0; i < gameBoard.length ; i++) {
for (int j = 0; j < gameBoard[0].length; j++) {
if(gameBoard[i][j] != null){
//set the origin of a potential new path
ArrayList<Point> potentialPath = new ArrayList<>();
Point origin = new Point (i,j);
if(!checkedPoints.contains(origin)) {
potentialPath.add(origin);
checkedPoints.add(origin);
potentialPath = findPath(gameBoard, i, j, potentialPath, gameBoard[i][j].getPaint(), checkedPoints, Neighbor.RIGHT.index); //Changed from Neighbor.DOWN.index
if (potentialPath != null) {
paths.add(new Path(potentialPath, gameBoard[i][j].getPaint()));
}
}
}
}
}
return paths;
}
private static ArrayList<Point> findPath(Stone[][] gameBoard, int x, int y, ArrayList<Point> path, Paint color, ArrayList<Point> checkedPoints, int cameFrom){
int startClockwiseScanAtDirection = cameFrom + 5;
for (int i = startClockwiseScanAtDirection; i < startClockwiseScanAtDirection + 7; i++) {
// avoid ArrayIndexOutOfBounds
if(x+neighbors[i%8].x < 0 || y+neighbors[i%8].y < 0 || x+neighbors[i%8].x >= gameBoard.length || y+neighbors[i%8].y >= gameBoard[0].length)
continue;
// check if there's a stone that matches the current stone, we're scanning around
if(gameBoard[x+neighbors[i%8].x][y+neighbors[i%8].y] != null && gameBoard[x+neighbors[i%8].x][y+neighbors[i%8].y].getPaint() == color){
// found one
Point nextStone = new Point(x+neighbors[i%8].x,y+neighbors[i%8].y);
// is the point we just found the origin of the path?
if(nextStone.equals(path.get(0)) && path.size() > MIN_PATH_LENGTH) { //This seems to prevent drawing a path when we have less stone to form a path with
path.add(nextStone);
checkedPoints.add(nextStone);
return path;
}
// otherwise if it's already part of the path ignore it
if (path.contains(nextStone)) {
continue;
}
// else add it to the path and keep going
path.add(nextStone);
checkedPoints.add(nextStone);
// recurse on the next stone in the path
ArrayList<Point> newPath = findPath(gameBoard,x+neighbors[i%8].x, y+neighbors[i%8].y, path, color, checkedPoints, i%8);
if (newPath == null){
// didn't find a way to continue, so backtrack
path.remove(path.size()-1);
} else {
// we have a completed path to return
return newPath;
}
}
}
return null;
}
}
Path class
public class Path {
public Paint getColor() {
return color;
}
public void setColor(Paint color) {
this.color = color;
}
public ArrayList<Point> getCoordinateList() {
return coordinateList;
}
public void setCoordinateList(ArrayList<Point> coordinateList) {
this.coordinateList = coordinateList;
}
private ArrayList<Point> coordinateList;
private Paint color;
public Path(ArrayList<Point> coordinatePath, Paint color){
this.coordinateList = coordinatePath;
this.color = color;
}
#Override
public String toString() {
return coordinateList.toString();
}
}
Here some case test:
Called in the MainActivity's onCreate():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gameGrid = findViewById(R.id.gameGrid);
bluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bluePaint.setStyle(Paint.Style.FILL_AND_STROKE);
bluePaint.setColor(Color.BLUE);
redPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setStyle(Paint.Style.FILL);
redPaint.setColor(Color.RED);
bgrBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgrBluePaint.setStyle(Paint.Style.STROKE);
bgrBluePaint.setStrokeWidth(bgrStrokeWdth);
bgrBluePaint.setColor(Color.BLUE);
bgrRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgrRedPaint.setStyle(Paint.Style.STROKE);
bgrRedPaint.setStrokeWidth(bgrStrokeWdth);
bgrRedPaint.setColor(Color.RED);
bluePlayer = new Stone(1,bluePaint, bgrBluePaint);
redPlayer = new Stone(2, redPaint, bgrRedPaint);
gameBoard = new Stone[100][100];
gameBoard[47][47]= redPlayer;
gameBoard[46][47]= bluePlayer;
gameBoard[44][48]= redPlayer; //REDs form a path when you place this stone in the last positioon
gameBoard[44][49]= redPlayer;
gameBoard[45][47]= redPlayer;
gameBoard[45][48]= bluePlayer;
gameBoard[45][49]= bluePlayer;
gameBoard[45][50]= redPlayer;
gameBoard[46][50]= bluePlayer;
gameBoard[46][49]= redPlayer;
gameBoard[46][48]= redPlayer;
gameBoard[47][50]= bluePlayer;
gameBoard[47][48]= bluePlayer;
gameBoard[47][49]= redPlayer;
gameBoard[48][50]= redPlayer;
gameBoard[48][49]= redPlayer;
gameBoard[48][48]= redPlayer;
gameBoard[49][50]= bluePlayer;
gameBoard[48][51]= redPlayer;
gameBoard[44][50] = bluePlayer;
ArrayList<Path> paths = Pather.findPaths(gameBoard);
gameGrid.setPaths(paths);
gameGrid.setGameBoard(gameBoard);
}
Placing stones at the following positions clears the path:
//Adding the following deletes the path
gameBoard[43][50] = redPlayer; //Adding this one in last position clears the path
gameBoard[45][51] = redPlayer;
I need to figure out how to make a condition that check for an opponent nearby first then validate the path.
EDIT 2:
Stone.java
public class Stone{
private int _player;
private Paint _paint, _bgrPaint;
public Stone(int player, Paint paint, Paint bgrPaint){
_player = player;
_paint = paint;
_bgrPaint = bgrPaint;
}
public int getPlayer() {
return _player;
}
public Paint getPaint() {
return _paint;
}
public Paint get_bgrPaint() {
return _bgrPaint;
}
}
Point.java
public class Point {
int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object point) {
return this.x == ((Point) point).x && this.y == ((Point) point).y;
}
#Override
public String toString() {
return "("+x+","+y+")";
}
}
Screenshoot of what a valid path should look
A more-or-less standard way to approach this kind of problem is a "sweep line" algorithm. For simplicity, say we're looking for blue paths wrapping red points.
(You can process red paths wrapping blue points at the same time or in a second pass, but you can work that out later.)
You can search for "sweep line algorithm" to see how they work in related applications. The Wikipedia page isn't bad.
For this problem, the sweep line is a set of y-intervals. It's initialized using the leftmost (least x) blue point(s). It gets one interval for each vertically adjacent set of blue points. Each interval represents a vertical slice through a potential blue polygon.
The rest of the algorithm is to design the rules needed to update the scan line when it is moved one position to the right, incrementing x. This will be a matter of updating each interval. When a step finds a disconnected set of vertically adjacent points, a new interval is added. In some cases, intervals will "die out" because the potential polygon boundary dead-ends (think of a C shape). In other cases, they will "merge" because, at the corresponding x-coordinate, there is a set of 1 or more vertically adjacent connecting points. In still other cases, the polygon will complete successfully with a final set of 1 or more vertically adjacent points.
The details will be fiddly, but not hard to work out by case analysis.
To trace successful polygons, intervals can include two chains of preceding points: the upper and lower polygon boundaries.
The last consideration is whether a successfully closed polygon encloses at least one red point. But this is easy. If for any x-coordinate, the interval representing a polygon bracketed a red point, then the answer is yes. This can be recorded as an initially false boolean maintained in the interval, which is set true every time such a red point is seen. When a polygon is successfully closed, check the flag to see whether it should be used or not.
All the above can be made efficient for very large grids by using suitable data structures: interval trees for example. But if the grid is comparatively small, it should be fine to use simple lists. At any rate, consider prototyping it with a list for the sweep line first first and optimize with more complicated data structures later if needed.
As I wrote in my comments, without mvce it is very hard to offer detailed help.
From what I see in the code I figure you are trying to map all cyclic single-color paths on the board.
I made some documented changes in the code, hoping (without being able to properly check it) that it may help you improve your code.
Note that as Stone class was not posted, I changed the representation of the board to int[][]
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Phather {
private static final int RED = 2, BLUE = 1;
private static final int MIN_PATH_LENGTH = 3;
public enum Neighbor{
UP_RIGHT ( 1,-1),
RIGHT ( 1, 0),
DOWN_RIGHT( 1, 1),
DOWN ( 0, 1),
DOWN_LEFT (-1, 1),
LEFT (-1, 0),
UP_LEFT (-1,-1),
UP ( 0,-1);
int x, y;
Neighbor(int x, int y){
this.x = x;
this.y = y;
}
}
public static Set<Path> findPaths(int[][] gameBoard){
//use set to prevent duplicate paths
Set<Path> paths = new HashSet<>();
for (int x = 0; x < gameBoard.length ; x++) {
for (int y = 0; y < gameBoard[0].length; y++) {
//note that array indexes are [y][x] while point arguments are x,y
if(gameBoard[y][x] != 0){
//use set to prevent duplicate elements. initialize it to allow for
//overlapping paths (paths that contain some shared points)
Set<Point> checkedPoints = new HashSet<>();
//set the origin of a potential new path
ArrayList<Point> potentialPath = new ArrayList<>();
Point origin = new Point (x,y);
if(checkedPoints.add(origin)) { //add returns false if duplicate
potentialPath.add(origin);
potentialPath = findPath(gameBoard, x, y, potentialPath, checkedPoints);
if (potentialPath != null) {
paths.add(new Path(potentialPath, gameBoard[y][x]));
}
}
}
}
}
return paths;
}
private static ArrayList<Point> findPath(int[][] gameBoard, int x, int y,
ArrayList<Point> path, Set<Point> checkedPoints){
int color = gameBoard[y][x]; //no need for color as argument. get from stone
for(Neighbor neighbor : Neighbor.values()) {
int neighborX = x + neighbor.x, neighborY = y + neighbor.y;
// avoid ArrayIndexOutOfBounds
//todo: refactor to method isValidAddress(x,y,maxX, maxY)
if((neighborX < 0) || ( neighborY < 0) || (neighborY >= gameBoard.length)
|| (neighborX >= gameBoard[0].length)) {
continue;
}
// check if there's a stone that matches the current stone, we're scanning around
if((gameBoard[neighborY][neighborX] != 0) && (gameBoard[neighborY][neighborX] == color)){
// found one
Point nextStone = new Point(neighborX,neighborY);
// is the point we just found the origin of the path ?
if(nextStone.equals(path.get(0)) && (path.size() > MIN_PATH_LENGTH)) {
path.add(nextStone); //do you want it in path twice ?
//checkedPoints.add(nextStone); //if added to path before, it is already in checkedPoints
return path;
}
// otherwise if it's already part of the path ignore it
if (path.contains(nextStone)) {
continue;
}
// else add it to the path and keep going
path.add(nextStone);
checkedPoints.add(nextStone);
// recurse on the next stone in the path
ArrayList<Point> newPath = findPath(gameBoard, neighborX, neighborY, path, checkedPoints);
if (newPath == null){
// didn't find a way to continue, so backtrack
path.remove(path.size()-1);
} else {
// we have a completed path to return
return newPath;
}
}
}
return null;
}
}
class Path {
private ArrayList<Point> coordinateList;
private int color;
Path(ArrayList<Point> coordinatePath, int color){
coordinateList = coordinatePath;
this.color = color;
}
int getColor() { return color; }
#Override
public String toString() {
return coordinateList.toString();
}
List<Point> getPoints() { return coordinateList; }
int size() { return coordinateList.size(); }
#Override
public boolean equals(Object p){
if (p == this) { return true; }
if (p == null) { return false;}
if (!(p instanceof Path)) {return false; }
Path path = (Path)p;
return getPoints().containsAll(path.getPoints())
&& path.getPoints().containsAll(getPoints());
}
}
I am currently working on an assignment to build a functioning Rubik's cube. The program does not need a GUI. But it must simulate a 3 X 3 cube with rotation behaviors and provide a graphical representation of the cube (I'm going with a flat lettered structure). My code has a class for facets which make a Face (another class) and then there is a cube class which contains the rotation methods.
I am having trouble creating/choosing an algorithm to use that would simulate the cube and all possible rotations accurately. I found a solution on this site referencing a paper proposing 7 different ways to do it (the link in below). But which method is the most intuitive/easy to code? And more importantly, which would best fit the behavior outlined below (in the pseudocode)?
I am having trouble understanding how I could use any method to account for the changes on each face at once especially when taking into account the behavior of the face rotations (as opposed to the rows and columns).
How would you represent a Rubik's Cube in code?
Rotation Pseudocode:
map each cube numbers 1-54, faces 1 – 4 are 1 – 36 while top face is 37 - 45 and bottom is 46 – 54
1 turn: clockwise = +9
1 turn: counterclockwise = -9 loops back to 45
1 turn: up = + 37
1 turn: down = -37 loops back to 46
2 turns: clockwise = +9(2)
2 turns: counterclockwise = -9(2)
2 turns: up = +37(2)
2 turns: down = -37(2)
3 turns: clockwise = +9(3)
3 turns: counterclockwise = -9(3)
3 turns: up = +37(3)
3 turns: down = -37(3)
This pseudocode does not account for the face changes.
Is there a better/easier way to do this, different from the method my pseudocode proposes? How do I account for the face changes?
Example: (front face, 1 turn, clockwise)
123 741
456 852
789 963
Note: I am leaning towards the 54 element vector but am unsure how to manipulate it.
Also, this is my first question so let me know if something is wrong (not enough info, too much, wrong topic, etc.)
Thank you!
Note: This is the code I am working with.
Facet Class:
public class Facets {
public Color color;
public Facets(Color color){
}
public enum Color {
B, G, R, Y, O, P
}
public String getName(){
return this.color.name();
}
}
Face Class:
import java.util.Arrays;
public class Face {
public Facets[] face;
/*public Face(Facets.Color color, Facets.Color[] array){
face = new Facets[9];
for(int i = 0; i < face.length; i++){
face[i] = new Facets(array[i]);
face[i] = new Facets(color);
}
}*/
public Face(Facets.Color color){
face = new Facets[9];
for(int i = 0; i < face.length; i++){
face[i] = new Facets(color);
}
}
public Face(Facets.Color[] array){
face = new Facets[9];
for (int i = 0; i < face.length; i++){
face[i] = new Facets(array[i]);
//face[i] = face[i].toString();
}
}
//Returns a textual representation of Face
public String printFace(){
StringBuilder faceString = new StringBuilder();
for(Facets f: face){
faceString.append(f.getName());
System.out.println(f.toString());
}
return faceString.toString();
}
public static void main(String[] args){
Face face = new Face(Facets.Color.B);
System.out.println(face.toString());
}
}
Cube Class:
public class Cube {
public Cube(Face front, Face right, Face back, Face left, Face top, Face bottom){
}
public Cube createCube(){
}
public Cube rotate(int row, int column, String direction, int turns){
/*Turns must be between 0 - 4
Row must be 1 or 2, column must be 1 or 2, direction must be clockwise, counterclockwise, up or down (0 means no turn, 1 is top row or left column; 2 is bottom row or right column)
*/
}
public int turns(){
}
public Cube row(){
}
public Cube column(){
}
public Cube clockwise(){
}
public Cube counterClockwise(){
}
public Cube up(){
}
public Cube down(){
}
public Cube random(Cube cube){
}
public Cube configuration(Cube cube){
}
}
Rubik's Cube:
public class RubiksCube {
public RubiksCube(Cube cube){
}
public RubiksCube(Face front, Face rightOfFront, Face back, Face leftOfFront, Face top, Face bottom){
}
//calls face and colors and initializes the arrays into a object
//default config: solid color on each side so each array, representing a face, is set to a solid color
public void createNewCube(){
}
public void rotation(Cube cube, int row, int column, String direction, int turns){
}
public Cube configuration(Cube cube){//should return 6 Faces? or cube?
return cube;
}
}
Think about what data structure makes it the easiest for you to conceptualize the cube. The different solutions in the link you provided all have pros and cons. You can have a more terse structure (i.e. the five integer representation) which takes up less memory and optimizes performance. But that representation might be difficult to work with if you are new to the problem. On the other end of the spectrum is an object oriented representation that models the way most people would think about a rubik cube. That might be the best approach for someone new to rubik's cubes.
After you have a data structure that makes sense to you, think about the different operations that can be performed on the cube. You can capture each of these moves in a function that alters the state of the data. If you have a real rubik cube to play with, figure out what all the different turns are and how they change the values of the rubik cube. Then try to model them in their own functions. i.e. a top wise turn would cause five of the six faces to change, right. It would cause four of the faces to receive the top row of their neighbour. And it would cause a shift in the top face as well. The bottom face would be uneffected.
If you are feeling overwhelmed with the complexity try to break your problem down into a smaller one. Maybe you can try to write a representation for a rubik cube that is 2 x 2 rather than 3 x 3. Once you've mastered the smaller problem, return to the larger one.
i just started to write some code to manipulate a cube.
package p;
import java.util.Arrays;
import static p.Facet.*;
enum Facet { // used for face, color, and operations
u(1),d(1),r(2),l(0),f(1),b(3); // maybe FRULBD instead?
Facet(int pad) {
this.pad=pad;
}
// u pad n+1
// l pad 0
// f pad n+1
// r pad 2(n+1)
// b pad 3(n+1) or at the bottom.
// d pad n+1
final int pad; // for formatting
}
class Face {
Face(int n,Facet facet) {
this.n=n;
this.facet=facet;
facets=new Facet[n][n];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
facets[i][j]=facet;
}
Face(Face face) {
this.n=face.n;
this.facet=face.facet;
facets=new Facet[n][n];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
facets[i][j]=face.facets[i][j];
}
#Override public String toString() {
return toString(0);
}
public String toString(int pad) {
StringBuffer stringBuffer=new StringBuffer();
for(int i=0;i<n;i++) {
stringBuffer.append(pad(pad,n));
for(int j=0;j<n;j++)
stringBuffer.append(facets[i][j]);
stringBuffer.append('\n');
}
return stringBuffer.toString();
}
String pad() {
return pad(facet.pad,n);
}
static String pad(int length) {
StringBuffer stringBuffer=new StringBuffer(length);
for(int i=0;i<length;i++)
stringBuffer.append(" ");
return stringBuffer.toString();
}
static String pad(int length,int n) {
return pad(length*(n+1));
}
#Override public int hashCode() {
final int prime=31;
int result=1;
result=prime*result+((facet==null)?0:facet.hashCode());
result=prime*result+Arrays.deepHashCode(facets);
result=prime*result+n;
return result;
}
#Override public boolean equals(Object obj) {
if(this==obj) return true;
if(obj==null) return false;
if(getClass()!=obj.getClass()) return false;
Face other=(Face)obj;
if(facet!=other.facet) return false;
if(!Arrays.deepEquals(facets,other.facets)) return false;
if(n!=other.n) return false;
return true;
}
static void rotateClockwise(Object[][] objects) {
Object temp;
int n=objects.length;
// For each concentric square around the middle of the matrix to rotate...
// This value will be used as (m, n) offset when moving in.
// Integer division by 2 will skip center if odd length.
for(int i=0;i<n/2;i++)
// for the length of this ring
for(int j=0;j<n-2*i-1;j++) {
temp=objects[i][i+j];
objects[i][i+j]=objects[n-i-j-1][i];
objects[n-i-j-1][i]=objects[n-i-1][n-i-j-1];
objects[n-i-1][n-i-j-1]=objects[i+j][n-i-1];
objects[i+j][n-i-1]=temp;
}
}
static void rotateCounterClockwise(Object[][] objects) {
int n=objects.length;
for(int i=0;i<n/2;i++) {
for(int j=i;j<n-i-1;j++) {
Object temp=objects[i][j];
objects[i][j]=objects[j][n-1-i]; // move values from right to top
objects[j][n-1-i]=objects[n-1-i][n-1-j]; // move values from bottom to right
objects[n-1-i][n-1-j]=objects[n-1-j][i]; // move values from left to bottom
objects[n-1-j][i]=temp;
}
}
}
final int n;
final Facet facet;
final Facet[][] facets;
}
class Cube {
Cube(int n) {
u=new Face(n,Facet.u);
d=new Face(n,Facet.d);
r=new Face(n,Facet.r);
l=new Face(n,Facet.l);
f=new Face(n,Facet.f);
b=new Face(n,Facet.b);
}
void rotatex(Face face,Face old) {
for(int i=0;i<face.n;i++)
for(int j=0;i<face.n;j++)
face.facets[i][j]=old.facets[i][j];
}
void rotateConterClockwise(Face f,Face u,Face l,Face d,Face r) {
for(int i=0;i<3;i++)
rotate(f,u,l,d,r);
}
void rotate(Face f,Face u,Face l,Face d,Face r) {
Face.rotateClockwise(f.facets);
Facet[] top=new Facet[u.n];
for(int i=0;i<u.n;i++)
top[i]=u.facets[u.n-1][i];
for(int i=0;i<u.n;i++) // move left to u
u.facets[u.n-1][i]=l.facets[u.n-i-1][u.n-1];
for(int i=0;i<u.n;i++) // bottom to left
l.facets[u.n-i-1][u.n-1]=d.facets[0][i];
for(int i=0;i<u.n;i++) // right to bottom
d.facets[0][i]=r.facets[i][0];
for(int i=0;i<u.n;i++)
r.facets[i][0]=top[i];
}
void op(Facet facet) {
switch(facet) {
case b:
rotate(b,u,r,d,l);
break;
case d:
rotate(d,f,l,b,r);
break;
case f:
rotate(f,u,l,d,r);
break;
case l:
rotate(l,u,b,d,f);
break;
case r:
rotate(r,u,f,d,b);
break;
case u:
rotate(u,d,l,f,r);
break;
}
}
void opInverse(Facet facet) {
switch(facet) {
case b:
rotateConterClockwise(b,u,r,d,l);
break;
case d:
rotateConterClockwise(d,f,l,b,r);
break;
case f:
rotateConterClockwise(f,u,l,d,r);
break;
case l:
rotateConterClockwise(l,u,b,d,f);
break;
case r:
rotateConterClockwise(r,u,f,d,b);
break;
case u:
rotateConterClockwise(u,d,l,f,r);
break;
}
}
#Override public int hashCode() {
final int prime=31;
int result=1;
result=prime*result+((b==null)?0:b.hashCode());
result=prime*result+((d==null)?0:d.hashCode());
result=prime*result+((f==null)?0:f.hashCode());
result=prime*result+((l==null)?0:l.hashCode());
result=prime*result+((r==null)?0:r.hashCode());
result=prime*result+((u==null)?0:u.hashCode());
return result;
}
#Override public boolean equals(Object obj) {
if(this==obj) return true;
if(obj==null) return false;
if(getClass()!=obj.getClass()) return false;
Cube other=(Cube)obj;
if(b==null) {
if(other.b!=null) return false;
} else if(!b.equals(other.b)) return false;
if(d==null) {
if(other.d!=null) return false;
} else if(!d.equals(other.d)) return false;
if(f==null) {
if(other.f!=null) return false;
} else if(!f.equals(other.f)) return false;
if(l==null) {
if(other.l!=null) return false;
} else if(!l.equals(other.l)) return false;
if(r==null) {
if(other.r!=null) return false;
} else if(!r.equals(other.r)) return false;
if(u==null) {
if(other.u!=null) return false;
} else if(!u.equals(other.u)) return false;
return true;
}
#Override public String toString() { //ulfrbd
StringBuffer stringBuffer=new StringBuffer(3*4*(u.n+1));
for(int i=0;i<u.n;i++) {
stringBuffer.append(u.pad());
for(int j=0;j<u.n;j++)
stringBuffer.append(u.facets[i][j]);
stringBuffer.append('\n');
}
for(int i=0;i<u.n;i++) {
stringBuffer.append(l.pad());
for(int j=0;j<u.n;j++)
stringBuffer.append(l.facets[i][j]);
stringBuffer.append(' ');
for(int j=0;j<u.n;j++)
stringBuffer.append(f.facets[i][j]);
stringBuffer.append(' ');
for(int j=0;j<u.n;j++)
stringBuffer.append(r.facets[i][j]);
stringBuffer.append(' ');
for(int j=0;j<u.n;j++)
stringBuffer.append(b.facets[i][j]);
stringBuffer.append('\n');
}
for(int i=0;i<u.n;i++) {
stringBuffer.append(d.pad());
for(int j=0;j<u.n;j++)
stringBuffer.append(d.facets[i][j]);
stringBuffer.append('\n');
}
return stringBuffer.toString();
}
final Face u,d,r,l,f,b;
}
public class Main {
public static void main(String[] args) {
Cube initial=new Cube(3);
Cube cube=new Cube(3);
System.out.println(cube.u.facets[0][0]);
System.out.println(cube.u);
System.out.println(cube);
for(int i=0;i<4;i++) {
cube.op(Facet.f);
System.out.println(cube);
}
// F' U L' U'
cube=new Cube(3);
int i=0;
do {
fpulpup(cube);
System.out.println(i);
System.out.println(cube);
i++;
} while(!cube.equals(initial));
}
private static void fpulpup(Cube cube) {
cube.opInverse(f);
cube.op(u);
cube.opInverse(l);
cube.opInverse(f);
}
}
i have a problem with one thing. I have a map of 10 cities and a civilian. I want the civilian to be walking from city to city randomly. But the problem is that the city is beeing chosen on and on so the civilian is changing the destination before he reachs it. This is my part of a code of a Jpanel where everything is drawn:
#Override
public void run() {
while (running) {
update();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException ex) {
}
}
}
private void update() {
if (game != null && running == true) {
c.goTo(cities); // c is civilian
}
}
and this is part of code for civilian
private boolean set = true;
public void move(int x, int y) {
if (this.location.x != x || this.location.y != y) {
if (this.location.x > x) {
this.location.x -= 1;
} else {
this.location.x += 1;
}
if (this.location.y > y) {
this.location.y -= 1;
} else {
this.location.y += 1;
}
}
}
public void goTo(ArrayList<City> cities) {
City city;
if (set) {
city = cities.get(rand());
move(city.location.x, city.location.y);
set = false;
} else {
set = true;
}
}
public int rand() {
int i;
Random rand = new Random();
i = rand.nextInt(10);
return i;
}
How to solve it ?
So, your problem is here:
while (running) {
update();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException ex) {
}
}
You're calling update every 17 milliseconds which in turn is causing your civilian to move to a new city every 17 milliseconds. You could make a separate statement that calls update while another boolean statement is false so that you travel only when he is in a city.
For example:
boolean travelling = //whatever you go about to configure this
while(travelling == false){
update();
}
This will cause him to only travel when he is not in a city. Here is some very rough code (you will have to configure it to your liking):
//civilian x //civilian y
if(this.location.x == //randomed city.x && this.location.y == //randomed city.y){
travelling = false;
}
This will most likely need to be within the run() method in your first set of code, so it can be checked over and over. But let me explain what the above code is doing:
First, you have a thread or something keeping it running checking if your civilian's x and y correspond to the most recently randomed city's x and y, obviously when they're the same, the civilian is at the city.
Second, when the x and y's are the same, the statement makes travelling false
Third, When travelling is false, your custom update method is called, picking a new city, at random and putting your civilian back on the move.
The method evalBoard() below takes brd, and copies it's parameters into pos. It then iterates through some possible values, making changes to pos by calling makeMove(). These calls to pos.makeMove are somehow changing the values inside brd as well. It can be seen clearly by stepping through the area I've marked in evalBoard. Why is this happening?
public Move evalBoard(Board brd) {
// evaluates current board and returns point of best move
Move best;
Move tmp = new Move(-1, -1, LOSE, brd.getPlayer());
if (brd.getPlayer() == PLAYER1) {
best = new Move(-1, -1, LOSE, PLAYER1);
} else {
best = new Move(-1, -1, WIN, PLAYER2);
}
// check if board is empty if so return optimal move
//
if (brd.isEmpty()) {
return (new Move(0, 0, 0, PLAYER1));
}
// iterate through possible moves
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 3; y++) {
if (brd.getSpace(x, y) == ' ') {
// copy all of brd to pos
pos=new Board(brd.getBoard(),brd.getPlayer());
tmp.setX(x);
tmp.setY(y);
tmp.setPlayer(brd.getPlayer());
/*
* problem can be seen by stepping through the next line. brd is changed at the same time.
*/
pos.makeMove(tmp);//Problem is here this call is changing brd as well
// evaluate for immediate win or loss
if (brd.getPlayer() == PLAYER1) {
if (pos.win()) {// immediate win
tmp.setValue(WIN);
return tmp;
} else { // not a winning move check it's recursive
// value
pos.setPlayer(PLAYER2);
tmp.copyFrom(evalBoard(pos));
if (tmp.getValue() >= best.getValue()) {
best.copyFrom(tmp);
}
}
} else {
if (pos.win()) {// immediate loss
tmp.setValue(LOSE);
return tmp;
} else {// not a losing move check it's recursive value
pos.setPlayer(PLAYER1);
tmp.copyFrom(evalBoard(pos));
if (tmp.getValue() <= best.getValue()) {
best.copyFrom(tmp);
}
}
}
}
}
}
return best;
}
Here's the Board class:
public class Board {
final boolean PLAYER1=true;
final boolean PLAYER2=false;
public char[][] board = new char[3][3];
public boolean Turn;
public Board(char[][] brd,boolean plr1){
board=brd;
Turn=plr1;
}
public char getPlayerChar(){
if (Turn==PLAYER1){
return 'X';
}else{
return 'O';
}
}
public char[][] getBoard(){
return board;
}
public void setBoard(char[][] brd){
board=brd;
}
public void displayBoard(){
for(int y=0;y<3;y++){
for(int x=0;x<3;x++){
System.out.print(board[x][y]);
if (x<2){
System.out.print(" | ");
}
}
System.out.println("");
if (y<2){
System.out.println("----------");
}
}
}
public void makeMove(Move mv){
//System.out.println("hit");
if (mv.getPlayer()==PLAYER1){
board[mv.getX()][mv.getY()]='X';
}else{
board[mv.getX()][mv.getY()]='O';
}
}
public void setPlayer(boolean plr){
Turn=plr;
}
public boolean getPlayer(){
return Turn;
}
public char getSpace(int x,int y){
return board[x][y];
}
public boolean isEmpty(){
for(int x=0;x<3;x++){
for(int y=0;y<3;y++){
if(!(board[x][y]==' ')){
return false;
}
}
}
return true;
}
public boolean win(){
char plrChar;
//returns true if board is winning position for current player
if (Turn==PLAYER1){
plrChar='X';
}else {
plrChar='O';
}
//Across
for(int y=0;y<3;y++){
if (board[0][y]+board[1][y]+board[2][y]==(plrChar+plrChar+plrChar)){
return true;
}
}
//Up/Down
for(int x=0;x<3;x++){
if (board[x][0]+board[x][1]+board[x][2]==(plrChar+plrChar+plrChar)){
return true;
}
}
//Diagonals
//top left to bottom right
if (board[0][0]+board[1][1]+board[2][2]==(plrChar+plrChar+plrChar)){
return true;
}
//bottom left to top right
if (board[0][2]+board[1][1]+board[2][0]==(plrChar+plrChar+plrChar)){
return true;
}
return false;
}
}
The problem is that when you do this:
pos=new Board(brd.getBoard(),brd.getPlayer());
you are creating a new Board (in pos) that shares its board array with the original Board (in brd). That's what the Board constructor is doing here:
public Board(char[][] brd,boolean plr1){
board=brd;
...
Naturally, since there is only one array, when you update it via one Board the changes are visible via the other Board.
If you don't want the old and new Board instances to share the same char array, then you should not assign like that. Instead, you should use a nested loop to copy the state from brd to board.
The Lesson to be learned
This is an example of a "leaky abstraction". The getBoard and setBoard methods allow code external to the Board to damage the state of a Board instance ... in a rather unexpected way. Non-leaky versions of these methods and the Board constructor would copy the contents of the array (may be to a new array) rather than allowing the Boards array instance to be accessible outside of the abstraction boundary.