I have an AP Computer Science assignment to make Tetris using GridWorld. I have to make 4 classes, TetrisBug, TetrisGame, TetrisBlock, and TetrisBlockO.
Here are the codes in that order:
TetrisBug.java
import info.gridworld.actor.*;
import info.gridworld.grid.*;
import java.util.ArrayList;
import java.awt.Color;
public class TetrisBug extends Bug
{
public TetrisBug(Color color)
{
super(color);
setDirection(180);
}
public void move()
{
Grid<Actor> gr = getGrid();
if (gr == null)
return;
Location loc = getLocation();
Location next = loc.getAdjacentLocation(getDirection());
if (gr.isValid(next))
moveTo(next);
else
removeSelfFromGrid();
}
public void act()
{
//this is empty for a reason.
}
}
TetrisGame.java
import info.gridworld.actor.*;
import info.gridworld.grid.*;
import java.util.ArrayList;
import java.awt.Color;
import info.gridworld.*;
public class TetrisGame {
public static ActorWorld world = new ActorWorld(new BoundedGrid<Actor>(19, 17));
public static TetrisBlock currentBlock;
public static int score;
public static void main(String[] args) {
//set up world
for (int i = 0; i < 19; i++) {
world.add(new Location(i,11),new Rock());
world.add(new Location(i,0),new Rock());
}
nextTetrisBlock();
//needed code for keyboard event handling
java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new java.awt.KeyEventDispatcher() {
public boolean dispatchKeyEvent(java.awt.event.KeyEvent event) {
String key = javax.swing.KeyStroke.getKeyStrokeForEvent(event).toString();
if (key.equals("pressed UP"))
currentBlock.rotate();
if (key.equals("pressed RIGHT"))
currentBlock.moveRight();
if (key.equals("pressed DOWN"))
currentBlock.act();
if (key.equals("pressed LEFT"))
currentBlock.moveLeft();
world.show();
return true;
}
});
world.show();
}
/**
* Calls removeCompleteRows and chooses a new TetrisBlock at random
*/
public static void nextTetrisBlock() {
removeCompleteRows();
TetrisBlock randomBlock = new TetrisBlock();//default 2block piece
//choose random block
int randNum = (int)(Math.random()*7)+1;//random number between 1 and 7
//if(randNum == 1)
// randomBlock = new TetrisBlockO();
//if(randNum == 2)
// randomBlock = new TetrisBlockI();
//if(randNum == 3)
// randomBlock = new TetrisBlockT();
//if(randNum == 4)
// randomBlock = new TetrisBlockL();
//if(randNum == 5)
// randomBlock = new TetrisBlock_L();
//if(randNum == 6)
// randomBlock = new TetrisBlockZ();
//if(randNum == 7)
// randomBlock = new TetrisBlock_Z();
currentBlock = randomBlock;
}
/**
* checks each row 1 through 18 (skip row 0) for full rows
* if a row is full, then remove the actor from each cell in that row
* and ask each actor located above the just deleted row to act and
* update the score++
*/
public static void removeCompleteRows() {
int columnsFilled = 0;
Grid grid = world.getGrid();
int row = 0;
//loops through rows only after each column has finished
for(row = grid.getNumRows()-1; row >= 0; row--) { //needed >=
columnsFilled = 0; //need to reinitialize this every iteration
for(int col = 0; col <= grid.getNumCols() - 1; col++) { //needed <=
if (grid.get(new Location(row,col)) != null) {
columnsFilled++;
}
}
if (columnsFilled == 10) {
for(int col = 0; col <= grid.getNumCols(); col++){
world.remove(new Location(row,col));
}
columnsFilled =0;
}
}
}
}
TetrisBlock.java
import info.gridworld.actor.*;
import info.gridworld.grid.*;
import java.util.ArrayList;
import java.awt.Color;
/**
* TetrisBlock is a type of Bug. It will act in GridWorld by moving down
* (direction 180) if it can, otherwise it will ask TetrisGame to make a new
* TetrisBlock for the game.
*/
public class TetrisBlock extends TetrisBug {
/**
* value of the current rotation position {0,1,2 or 3}
*/
protected int rotationPos;
/**
* blocks will have three TetrisBug objects in it... they will be added in the
* constructor
*/
protected ArrayList<TetrisBug> blocks;
/**
* used as a convenient reference to the Grid
*/
protected Grid<Actor> gr;
/**
* default constructor
*/
public TetrisBlock() {
super(Color.blue);
rotationPos = 0;
gr = TetrisGame.world.getGrid();
// ==> LAMEST GAME OVER EVER !!! <==
// if the Grid does not have room for the TetrisBlock.. GameOver
if (gr.get(new Location(0, 5)) != null
|| gr.get(new Location(1, 5)) != null) {
javax.swing.JOptionPane.showMessageDialog(null, "Score: "
+ TetrisGame.score, "GAME OVER!", 0);
System.exit(0);
}
putSelfInGrid(gr, new Location(1, 5));
blocks = new ArrayList<TetrisBug>();
TetrisBug a;
// create TetrisBugs for ArrayList blocks and put them in Grid gr
a = new TetrisBug(Color.blue);
a.putSelfInGrid(gr, new Location(0, 5));
blocks.add(a);
// TetrisBlock subclasses will add two more TetrisBug objects to blocks
}
/**
* TetrisBlock and its TetrisBugs must face down (direction 180) If they can
* move down, they will. Otherwise, it will ask TetrisGame to create a new
* TetrisBlock since this one is stuck at the bottom.
*/
public void act() {
setDirection(180);
for (TetrisBug tb : blocks)
tb.setDirection(180);
if (canMoveDown())
moveDown();
else {
if (!TetrisGame.currentBlock.canMoveDown())
TetrisGame.nextTetrisBlock();
}
}
/**
* Move the TetrisBlock and its TetrisBugs one cell. (they should already be
* facing down) Note: The order in which all the TetrisBugs move is important
* and depends on the current rotationPos.
*/
public void moveDown() {
if (rotationPos == 0) {
move();
blocks.get(0).move();
} else if (rotationPos == 1) {
blocks.get(0).move();
move();
}
}
/**
* Returns true if the TetrisBlock and its TetrisBugs can move (they should
* already be facing down) Otherwise, returns false.
*/
public boolean canMoveDown() {
if (rotationPos == 0)
return canMove();
else if (rotationPos == 1)
return canMove() && blocks.get(0).canMove();
else
return true;
}
/**
* Sets the direction of the TetrisBlock and its TetrisBugs to 90 (right) If
* they can move, they will move one cell (to the right)
*/
public void moveRight() {
setDirection(90);
for (TetrisBug tb : blocks)
tb.setDirection(90);
if (rotationPos == 0) {
if (canMove() && blocks.get(0).canMove()) {
blocks.get(0).move();
move();
}
} else if (rotationPos == 1) {
if (canMove()) {
move();
blocks.get(0).move();
}
}
}
/**
* Sets the direction of the TetrisBlock and its TetrisBugs to 90 (right) If
* they can move, they will move one cell (to the right)
*/
public void moveLeft() {
setDirection(270);
for (TetrisBug tb : blocks)
tb.setDirection(270);
if (rotationPos == 0) {
if (canMove() && blocks.get(0).canMove()) {
blocks.get(0).move();
move();
}
} else if (rotationPos == 1) {
if (canMove() && blocks.get(0).canMove()) {
blocks.get(0).move();
move();
}
}
}
/**
* If the TetrisBlock and its TetrisBugs can rotate, then they will all move
* to their proper location for the given rotation designated by
* rotationPos... Update rotationPos.
*/
public void rotate() {
Location nextLoc;
if (rotationPos == 0) {
// only one block must move
nextLoc = new Location(getLocation().getRow() - 1,
getLocation().getCol() + 1);
if (gr.isValid(nextLoc) && gr.get(nextLoc) == null) {
moveTo(nextLoc);
rotationPos = (rotationPos + 1) % 2;// will be % 4 with 4 blocks
}
} else if (rotationPos == 1) {
nextLoc = new Location(getLocation().getRow() + 1,
getLocation().getCol() - 1);
if (gr.isValid(nextLoc) && gr.get(nextLoc) == null) {
moveTo(nextLoc);
rotationPos = (rotationPos - 1) % 2;// will be % 4 with 4 blocks
}
}
}
}
TetrisBlockO.java
public class TetrisBlockO{
public TetrisBlockO() {
rotationPos = 0;
gr = TetrisGame.world.getGrid();
//GAME OVER!
if (gr.get(new Location(0, 5)) != null
|| gr.get(new Location(1, 5)) != null
|| gr.get(new Location(0, 6)) != null
|| gr.get(new Location(1, 6)) != null) {
javax.swing.JOptionPane.showMessageDialog(null, "Score: " + TetrisGame.score, "GAME OVER!", 0);
System.exit(0);
}
putSelfInGrid(gr, new Location(1, 5));
blocks = new ArrayList<TetrisBrick>();
TetrisBrick a;
a = new TetrisBrick(Color.blue);
a.putSelfInGrid(gr, new Location(0, 5));
blocks.add(a);
TetrisBrick b;
b = new TetrisBrick(Color.blue);
b.putSelfInGrid(gr, new Location(1, 6));
blocks.add(b);
TetrisBrick c;
c = new TetrisBrick(Color.blue);
c.putSelfInGrid(gr, new Location(0, 6));
blocks.add(c);
}
}
The first problem I'm having is in TetrisGame. The last method, to be precise. The grid.length statement is refusing to compile, and when I added a Grid grid = new Grid(); statement, it said it was abstract method and couldn't be instantiated. And compiling any of them give me a compiler warning,
\GridworldTetris\TetrisGame.java uses unchecked or unsafe operations.
Recompile with -Xlint:unchecked for details
The second problem is with the rotate method in TetrisBlock. I am not sure whether it works or not since the TetrisGame wont compile. I cant test it, but a friend of mine keeps telling me it's wrong, though IDK whether to believe him. Some validation would be nice.
Anyway, I have no real time limit on this, but I'd really like to be done with it. Thanks.
~Keelen
UPDATE: Thanks to user3580294 I solved the grid problem. But I found another, GRID DOESNT HAVE A LENGTH METHOD... I know what I was trying to do WOULD work, but cant find a way to do it, besides grid.length, which is invalid. Could someone make me a method that'd successfully remove completed rows? I really am stuck here... And I want to move to the next project...
For your removeCompleteRows() method try this in your if statement:
if (columnsFilled == 10) {
for(int col = 1; col < grid.getNumCols() - 1; col++){
grid.get(new Location( row,col )).removeSelfFromGrid();
}
columnsFilled = 0;
}
I think the World remove() method might work, but generally you won't have access to the World your Actor is on, so using Grid's removeSelfFromGrid() is safer.
Also, I assume you only want to remove the Blocks not your walls so you only want to iterate through Locations your Blocks might occupy, 1 through getNumCols() - 1.
Related
Im trying to write a code and i used the do while loop in an atempt to loop my main code after the user types in y or yes (case insensitive) into an InputDialog.
here is the code, the only error is that a } needs to be put in after the do { but eve if i put it into the place I want, it doesn't work, also, it does not recognize thw while statement at the bottom, please help.
package tictactoemain;
import javax.swing.JOptionPane;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
/**
* JFrame to hold TicTacToe board.
*/
public class TicTacToeFrame extends JFrame
{
**do {** //do starts here
private static final long serialVersionUID = 1L;
// Indicate whose turn it is
private char whoseTurn = 'X';
private boolean gameOver = false;
// Create cell grid using an Array
private Cell[][] cells = new Cell[3][3];
// Create a status label
JLabel jlblStatus = new JLabel("X's turn to play");
/**
* No-argument Constructor
* #return
*/
public TicTacToeFrame()
{
// Panel to hold cells
JPanel panel = new JPanel(new GridLayout(3, 3, 0, 0));
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
panel.add(cells[i][j] = new Cell());
panel.setBorder(new LineBorder(Color.black, 1));
jlblStatus.setBorder(new LineBorder(Color.black, 1));
add(panel, BorderLayout.CENTER);
add(jlblStatus, BorderLayout.SOUTH);
}
// Determine if it's a tie
/**
* Determine if game board is full.
* #return True, if game board is full. Otherwise, false.
*/
public boolean isFull()
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (cells[i][j].getToken() == ' ')
return false;
return true;
}
// Check to see if a player (Token) has won
/**
* Determines if a given token has won.
* #param token Token to test for winning
* #return True, if the token has won. Otherwise, false.
*/
public boolean isWon(char token)
{
// check rows
for (int i = 0; i < 3; i++)
if ((cells[i][0].getToken() == token)
&& (cells[i][1].getToken() == token)
&& (cells[i][2].getToken() == token))
{
return true;
}
// check columns
for (int j = 0; j < 3; j++)
if ((cells[0][j].getToken() == token)
&& (cells[1][j].getToken() == token)
&& (cells[2][j].getToken() == token))
{
return true;
}
// check diagonals
if ((cells[0][0].getToken() == token)
&& (cells[1][1].getToken() == token)
&& (cells[2][2].getToken() == token))
{
return true;
}
if ((cells[0][2].getToken() == token)
&& (cells[1][1].getToken() == token)
&& (cells[2][0].getToken() == token))
{
return true;
}
return false;
}
/**
* Defines a cell in a TicTacToe game board.
*/
public class Cell extends JPanel
{
/**
*
*/
private static final long serialVersionUID = 1L;
// token of this cell
private char token = ' ';
/**
* Constructor
*/
public Cell()
{
setBorder(new LineBorder(Color.black, 1));
addMouseListener(new MyMouseListener());
}
/**
* Gets the token of the cell.
* #return The token value of the cell.
*/
public char getToken()
{
return token;
}
/**
* Sets the token of the cell.
* #param c Character to use as token value.
*/
public void setToken(char c)
{
token = c;
repaint();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
if (token == 'X')
{
g.drawLine(10, 10, getWidth() - 10, getHeight() - 10);
g.drawLine(getWidth() - 10, 10, 10, getHeight() - 10);
}
else if (token == 'O')
{
g.drawOval(10, 10, getWidth() - 20, getHeight() - 20);
}
}
// MouseListener to listen for a click to place a token
private class MyMouseListener extends MouseAdapter
{
private String playAgain;
private Object playYes;
private Object playNo;
#Override
public void mouseClicked(MouseEvent e)
{
if (gameOver)
return;
// if condition not met, the game is not over
if (token == ' ' && whoseTurn != ' ')
setToken(whoseTurn);
// Check game status
if (isWon(whoseTurn))
{
jlblStatus.setText("Congratulations " + whoseTurn + ", You Are Winnner! Game Over! Exit And Restart The Program To Play Again!" );
whoseTurn = ' ';
gameOver = true;
}
else if (isFull())
{
jlblStatus.setText("Tie game! Game over!");
whoseTurn = ' ';
gameOver = true;
}
else
{
whoseTurn = (whoseTurn == 'X') ? 'O' : 'X';
jlblStatus.setText(whoseTurn + "'s turn to play.");
}
// and ends here, while statement is below
{
// if the game is over, this will run
if (gameOver == true)
playAgain = JOptionPane.showInputDialog("Would you like to play again?");
// if statement that either restarts the game or closes it depending on the yes/y (cases are ignored) answer
while (playAgain.equalsIgnoreCase("y") | playAgain.equalsIgnoreCase("yes"))
{
playYes = ("Yes? Okay, have fun!");
JOptionPane.showMessageDialog( null, playYes );
}
}
}// End of Win condition checker
} // End class MyMouseListener
}
} // End class TicTacToeFrame
You could initialize all of your variables from a single method. This way you could initialize the game at the beginning of the program with it and if the player requests to play the game again you could call the same method thus bringing the program back to its original state.
private void init() {
whoseTurn = 'X';
gameOver = false;
jlblStatus = new JLabel("X's turn to play");
for(Cell[] ca : cells) {
for(Cell c : ca) {
c.setToken(' ');
}
}
}
My example might not include all variables but you could tweak it to suit your needs.
The do-while loop is not recognized because it is outside of a method.
Hi I am implementing an A* search algorithm based on WikiLink but I am not getting an answer from my search at all. any hints and/or help on what I may be doing wrong
I get a map that contains dirt and obstacles, I need to travel to all dirt particles and hoover them up and return to my starting point
Search Function
private void search_Astar(State start){ // we have confirmed a valid start state
// needed vars
Map<State, Integer> g_score = new HashMap<>();
Map<State, Integer> f_score = new HashMap<>();
Queue<State> openQueue = new PriorityQueue<State>(((State o1, State o2) -> f_score.get(o1) - f_score.get(o2)));
HashSet<State> closedSet = new HashSet<>();
// inits
g_score.put(start, 0);
f_score.put(start, start.heuristic());
openQueue.add(start);
// search
while (!openQueue.isEmpty()){
State current = openQueue.poll();
if (current.isGoal()){
// create path
solutionStack.push(Actions.Turn_Off);
while (current.takenAction != null){
solutionStack.push(current.takenAction);
current = current.parent;
}
return;
}
closedSet.add(current);
for (String action:current.legalActions()){
State child = current.CreateState(action);
if (closedSet.contains(child)) continue;
int tentativeG = g_score.get(current) + 1; // the cost to take an action
if (!openQueue.contains(child)) openQueue.add(child);
else if (tentativeG >= g_score.getOrDefault(child, Integer.MAX_VALUE)) continue;
g_score.put(child, tentativeG);
f_score.put(child, tentativeG + child.heuristic());
}
}
}
and State class (sorry for huge-ness)
import java.util.ArrayList;
import java.util.Collection;
public class State {
// keep track of position and orientation
public Orientation orientation;
public Position position;
public boolean turned_on;
// track world state
private Collection<Position> dirt;
private Collection<Position> obstacles;
private Position size;
// variables for detirmining goal state and heuristic
private Position home;
// for calculating G
public State parent;
public String takenAction;
public State(Position position, Orientation orientation, boolean turned_on, Collection dirt, Collection obs, Position home,
Position size, State parent, String action) {
this.position = position;
this.orientation = orientation;
this.turned_on = turned_on;
this.dirt = dirt;
this.obstacles = obs;
this.home = home;
this.size = size;
this.parent = parent;
this.takenAction = action;
}
public int G(){
State curr = parent;
int total = 0;
while (curr != null){
total += 1;
curr = parent.parent;
}
return total;
}
public String toString() {
return "State{position: " + position + ", orientation: " + orientation + ", on:" + turned_on + "}";
}
#Override
public int hashCode(){
int hashVal = 23;
hashVal = ((hashVal + position.x) << 5) - (hashVal + position.x);
hashVal = ((hashVal + position.y) << 5) - (hashVal + position.y);
if(turned_on){
hashVal += 1243;
}
if (orientation == Orientation.NORTH){
hashVal += 12;
}else if (orientation == Orientation.EAST){
hashVal += 2234;
}else if (orientation == Orientation.WEST){
hashVal += 32345;
}
return hashVal + dirt.size();
}
#Override
public boolean equals(Object o){
if (o instanceof State){
State other = (State)o;
if(!other.position.equals(this.position)){
return false;
}else if( other.turned_on != this.turned_on){
return false;
}else if( other.orientation != this.orientation){
return false;
}else if (other.dirt.size() != this.dirt.size()){
return false;
} else{
return true;
}
}else {
return false;
}
}
/**
* check to see if current node is a valid goal node
*
* #return true if valid, false otherwise
*/
public Boolean isGoal(){
return dirt.isEmpty() && position.equals(home);
}
/**
* Calculate all legal moves from current state
*
* #return Collection of legal actions (as string)
*/
public Collection<String> legalActions(){
Collection<String> actions = new ArrayList<String>();
if (!turned_on){
actions.add(Actions.Turn_On);
}else{
// no reason we couldn't turn
actions.add(Actions.Turn_Left);
actions.add(Actions.Turn_Right);
if (dirt.contains(position)){
actions.add(Actions.Suck);
}
// check if we can move forward
// ATTENTION this must be at bottom of function
if (orientation == Orientation.NORTH){
for (Position o: obstacles) {
if (o.y == position.y + 1 && o.x == position.x || position.y == size.y){
return actions;
}
}
// we have checked every obstacle and we would not collide
actions.add(Actions.Go);
}else if (orientation == Orientation.SOUTH){
for (Position o: obstacles) {
if (o.y == position.y - 1 && o.x == position.x || position.y == 1){
return actions;
}
}
actions.add(Actions.Go);
}else if (orientation == Orientation.WEST){
for (Position o: obstacles) {
if (o.x == position.x - 1 && o.y == position.y|| position.x == 1){
return actions;
}
}
actions.add(Actions.Go);
}else {
// we are pointing east
for (Position o: obstacles) {
if (o.x == position.x + 1 && o.y == position.y|| position.x == size.x){
return actions;
}
}
actions.add(Actions.Go);
}
}
return actions;
}
/**
* return a value to determine how optimal this state is
*
* evaluation methood: number of dirt left times a constant
* which is the added to the distance to the closest dirt
* if there are no dirts left it is the mannhatan distance to home
*
* note: should add state depth?
* #return int
*/
public int heuristic(){
int h = 0;
for (Position p:obstacles){
h += mannhatandist(p);
}
h += mannhatandist(home);
return h + dirt.size();
}
private int mannhatandist(Position p){
return Math.abs(p.x - position.x) + Math.abs(p.y - position.y);
}
private int distToClosest(){
int min = Integer.MAX_VALUE;
if (dirt.isEmpty()){
int dist = Math.abs(home.x - position.x) + Math.abs(home.y - position.y);
return dist;
}
for (Position p: dirt) {
int dist = Math.abs(p.x - position.x) + Math.abs(p.y - position.y);
min = Math.min(min, dist);
}
if (!turned_on) min++;
return min;
}
public State CreateState(String action) {
//Copy the "old" values from the current state.
//Before any action done, the "new" state has the same values.
Position new_position = new Position(position.x, position.y);
Orientation new_orientation = orientation;
boolean new_turned_on = turned_on;
Collection new_dirt = new ArrayList(dirt);
State new_parent = this;
//If the action is to turn the robot on, the turned_on variable for the new state takes the value "true".
if(action == "TURN_ON") {
new_turned_on = true;
}
//If the action is to turn the robot on, the turned_on variable for the new state takes the value "true".
else if(action == "TURN_OFF") {
new_turned_on = false;
}
//If the action is to suck, the current position needs to be taken out of the new state's dirt collection.
else if(action == "SUCK") {
new_dirt.remove(position);
}
//If the action is to go it depends on the orientation of the robot how the position of the new state will change.
else if(action == "GO") {
//If it's facing north, the y position increases by one, considering the old state and so on.
if(orientation == Orientation.NORTH) {
new_position.y++;
}
else if(orientation == Orientation.EAST) {
new_position.x++;
}
else if(orientation == Orientation.SOUTH) {
new_position.y--;
}
else {
new_position.x--;
}
}
//If the action is to turn left it depends on the orientation of the robot in the current state what the new orientation will be.
else if(action == "TURN_LEFT") {
if(orientation == Orientation.NORTH) {
new_orientation = Orientation.WEST;
}
else if(orientation == Orientation.EAST) {
new_orientation = Orientation.NORTH;
}
else if(orientation == Orientation.SOUTH) {
new_orientation = Orientation.EAST;
}
else {
new_orientation = Orientation.SOUTH;
}
}
//If the action is to turn right it depends on the orientation of the robot in the current state what the new orientation will be.
else if(action == "TURN_RIGHT") {
if(orientation == Orientation.NORTH) {
new_orientation = Orientation.EAST;
}
else if(orientation == Orientation.EAST) {
new_orientation = Orientation.SOUTH;
}
else if(orientation == Orientation.SOUTH) {
new_orientation = Orientation.WEST;
}
else {
new_orientation = Orientation.NORTH;
}
}
//Make a new state from the new variables that have been changed according to the action done.
State new_state = new State(new_position, new_orientation, new_turned_on, new_dirt, obstacles, home, size, new_parent, action);
return new_state;
}
}
I have Fixed it now. the problem was that my heuristic was not consistent. I have modified it and it is performing correctly now
I will not share my answer since this was for a school project and since the automatic anti-cheating system at school will probably detect this I will add my school username to this comment (dagur13)
p.s. I will post my updated code when I have recieved a grade for the project
I am working on a rock paper scissors game for my programming class and the professor wanted us to use a hash map to store the user's patterns and the times the pattern occurred in the hash map.
So I created a Pattern class that holds an array of values and in the Computer class I store it inside a hash map. The way I intended for the program to work is that the program will first generate a move based on the patterns in the hash map. If the map is empty then it would just generate a random move. Then after the user has made his move, his move will be put into an array to make a new pattern and the pattern will be saved to the hash map. If the pattern is already inside the map then the number of times it occurred will increase.
The predicted move is made by comparing the last three moves of the user with the patterns in the map and see which move will the user potentially throw next. So if the user last 4 moves were: R P S R then the program will take P S R and then add in R P S and see if those patterns are in the map. If they are, it will see which one is most likely to occur. Then if the user plays R next, the array will be updated into P S R R and the pattern continues.
So beginner mode is to start with an empty map and veteran is to load a previously saved map. However, I ran into some problems:
After I put the Pattern and times into the hash map, when I tried to iterate through it and see what patterns is it storing inside the map, I see that all patterns are the same and that does not suppose to happen. The pattern is suppose to be: R -> R P - > R P S (if the user throws rock, paper, scissors respectively) but now it just shows R P S -> R P S -> R P S. This can be seen in the getSize() in Computer.
I ran into a NullPointerException after the 4th move. The problem might be solved if I can solve the previous question but I have no idea why it happens.
I get a warning when I tried to read the map from a file so I was just wondering if the warning could potentially mess with the program.
Unchecked cast from Object to HashMap<Pattern, Integer>
Some help or pointers of what went wrong in my program would be greatly appreciated.
Computer:
import java.io.*;
import java.util.*;
public class Computer {
/**
* The hashmap that will holds the pattern and how many times it occured.
*/
private HashMap<Pattern, Integer> map;
/**
* Constructor
*/
public Computer() {
map = new HashMap<Pattern, Integer>();
}
/**
* Storing the pattern to the map.
*
* #param p
* The pattern that will be saved to the map.
*/
public void storePattern(Pattern p) {
Integer time = map.get(p);
// If time is null then the Pattern is not yet in the hashmap
if (time == null) {
map.put(p, 1);
} else {
map.put(p, time + 1);
}
}
/**
* Generating the computer's next move.
*
* #return The move that the computer will make.
*/
public char generateMove(Pattern user) {
int r = 0, p = 0, s = 0;
char returns = 'a';
if (!map.isEmpty()) {
char[] userPatts = user.getPattern();
char[] patts = userPatts.clone();
patts[patts.length - 1] = 'R';
Pattern testPatt = new Pattern(patts);
if (map.containsKey(testPatt))
r = map.get(patts);
patts[patts.length - 1] = 'P';
testPatt = new Pattern(patts);
if (map.containsKey(testPatt))
p = map.get(patts);
patts[patts.length - 1] = 'S';
testPatt = new Pattern(patts);
if (map.containsKey(testPatt))
s = map.get(patts);
if ((s - r) > 0 && (s - p) > 0)
return 'R';
if ((p - s) > 0 && (p - r) > 0)
return 'S';
if ((r - s) > 0 && (r - p) > 0)
return 'P';
if (s == r && r != 0)
return 'P';
if (s == p && s != 0)
return 'R';
if (r == p && p != 0)
return 'S';
}
// Throwing a random move
int max = (int) (Math.random() * 3) + 1;
if (max == 1)
returns = 'P';
else if (max == 2)
returns = 'S';
else if (max == 3)
returns = 'R';
return returns;
}
/**
* Loading the hashmap from a file.
*/
public void loadMap() {
File f = new File("HashMap.dat");
if (f.exists()) {
try {
ObjectInputStream in = new ObjectInputStream(
new FileInputStream(f));
map = (HashMap<Pattern, Integer>) in.readObject();
System.out.println("Successfully loaded.");
in.close();
} catch (IOException e) {
System.out.println("Error processing file.");
} catch (ClassNotFoundException e) {
System.out.println("Could not find class.");
}
}
}
/**
* Saving the hashmap to a file.
*/
public void saveMap() {
File f = new File("HashMap.dat");
try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream(f));
out.writeObject(map);
System.out.println("Map saved.");
out.close();
} catch (IOException e) {
System.out.println("Error processing file.");
}
}
public void getSize() {
System.out.println("Map size: " + map.size());
for (Map.Entry<Pattern, Integer> entry : map.entrySet()) {
Pattern b = entry.getKey();
char[] a = b.getPattern();
for (int i = 0; i < a.length; i++) {// Why a.length allows i to go
// from 0 to 3 if a.length == 4?
System.out.print(a[i] + " ");// Why are all the patterns the
// same?
}
System.out.println();
}
}
}
Pattern:
import java.io.Serializable;
import java.util.Arrays;
public class Pattern implements Serializable {
/**
* Array that holds the patterns.
*/
private char[] pattern;
/**
* Constructor.
*/
public Pattern(char[] patt) {
pattern = patt;
}
/**
* Getting the pattern array.
*
* #return The pattern array.
*/
public char[] getPattern() {
return pattern;
}
/**
* Override the hashCode().
*/
#Override
public int hashCode() {
return Arrays.hashCode(pattern);
}
/**
* Override the equals()
*/
#Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Pattern)) {
return false;
}
Pattern s = (Pattern) o;
return Arrays.equals(s.getPattern(), pattern);
}
}
Main:
import java.util.Scanner;
/**
* This program allows the user to play Rock Paper Scisors with a computer with
* a twist: The computer will try to predict the user's next move and try to
* beat it.
*
* #author:
*/
public class RockPaperScisors {
public static void main(String[] args) {
char computer = 'S';
int playerScore = 0, compScore = 0, tie = 0, full = 0;
char[] patt = new char[4];
Computer comp = new Computer();
boolean stop = false;
System.out
.println("Do you want to play veteran or beginner mode?\n1. Veteran\n2. Beginner");
int mode = input(2, 1);
if (mode == 1)
comp.loadMap();
comp.getSize();
while (!stop) {
// Generate computer's move.
computer = comp.generateMove(new Pattern(patt));
System.out.println("Enter R P S. Enter Q to quit.");
char a = input();
if (a == 'Q') {
stop = true;
break;
}
System.out.println("You threw: " + a);
if (full <= (patt.length - 1)) {
patt[full] = a;
full++;
} else {
for (int i = 0; i <= patt.length - 2; i++) {
patt[i] = patt[i + 1];
}
patt[patt.length - 1] = a;
}
for (int i = 0; i <= patt.length - 1; i++) {
System.out.print(patt[i]);
}
System.out.println();
// Store the new pattern
comp.storePattern(new Pattern(patt));
System.out.println("Computer plays: " + computer);
// Check for win or tie
if (a == computer) {
System.out.println("Tie.");
tie++;
} else {
if (a == 'R' && computer == 'P') {
System.out.println("Computer wins.");
compScore++;
}
if (a == 'R' && computer == 'S') {
System.out.println("Player wins.");
playerScore++;
}
if (a == 'P' && computer == 'S') {
System.out.println("Computer wins.");
compScore++;
}
if (a == 'P' && computer == 'R') {
System.out.println("Player wins.");
playerScore++;
}
if (a == 'S' && computer == 'R') {
System.out.println("Computer wins.");
compScore++;
}
if (a == 'S' && computer == 'P') {
System.out.println("Player wins.");
playerScore++;
}
}
// Saving the map
comp.saveMap();
comp.getSize();
System.out.println("Your score: " + playerScore + "\tTie: " + tie
+ "\tComputer score: " + compScore);
}
System.out.println("Thank you for playing.");
}
public static int input(int upper, int lower) {
Scanner in = new Scanner(System.in);
boolean valid = false;
int validInt = 0;
while (!valid) {
if (in.hasNextInt()) {
validInt = in.nextInt();
if (validInt <= upper && validInt >= lower) {
valid = true;
} else {
System.out.print("Invalid- Retry: ");
}
} else {
in.next();
System.out.print("Invalid input- Retry: ");
}
}
return validInt;
}
public static char input() {
Scanner in = new Scanner(System.in);
boolean valid = false;
char validChar = 'a';
while (!valid) {
if (in.hasNext()) {
validChar = in.next().charAt(0);
if (validChar == 'R' || validChar == 'P' || validChar == 'S'
|| validChar == 'Q') {
valid = true;
} else {
System.out.print("Invalid- Retry: ");
}
} else {
in.next();
System.out.print("Invalid input- Retry: ");
}
}
return validChar;
}
}
Why a.length allows i to go from 0 to 3 if a.length == 4?
In computer science you start counting at 0 so a length of 4 is
int array = new array[4];
array[0];
array[1];
array[2];
array[3];
Why are all the patterns the same?
in your main inside while (!stop) you should try patt = new char[4]; to ensure you don't use the same reference to that array over and over, because changing the base object will change all references aswell.
Just to clarify what i mean by references:
Is Java “pass-by-reference” or “pass-by-value”?
I am working on a robot maze where the robot finds the target without bumping into walls. As a "backtrack" method, I need the robot to go in the opposite direction as it did when it first came across a junction. This is my code:
import uk.ac.warwick.dcs.maze.logic.IRobot;
import java.util.ArrayList;
import java.util.*;
import java.util.Iterator;
public class Explorer {
private int pollRun = 0; // Incremented after each pass.
private RobotData robotData; // Data store for junctions.
private ArrayList<Integer> nonWallDirections;
private ArrayList<Integer> passageDirections;
private ArrayList<Integer> beenbeforeDirections;
private Random random = new Random();
int [] directions = {IRobot.AHEAD, IRobot.LEFT, IRobot.RIGHT, IRobot.BEHIND};
public void controlRobot (IRobot robot) {
// On the first move of the first run of a new maze.
if ((robot.getRuns() == 0) && (pollRun ==0))
robotData = new RobotData();
pollRun++; /* Increment poll run so that the data is not reset
each time the robot moves. */
int exits = nonwallExits(robot);
int direction;
nonWallDirections = new ArrayList<Integer>();
passageDirections = new ArrayList<Integer>();
beenbeforeDirections = new ArrayList<Integer>();
// Adding each direction to the appropriate state ArrayList.
for(int item : directions) {
if(robot.look(item) != IRobot.WALL) {
nonWallDirections.add(item);
}
}
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
passageDirections.add(item);
}
}
for(int item : directions) {
if(robot.look(item) == IRobot.BEENBEFORE) {
beenbeforeDirections.add(item);
}
}
// Calling the appropriate method depending on the number of exits.
if (exits < 2) {
direction = deadEnd(robot);
} else if (exits == 2) {
direction = corridor(robot);
} else {
direction = junction(robot);
robotData.addJunction(robot);
robotData.printJunction(robot);
}
robot.face(direction);
}
/* The specification advised to have to seperate controls: Explorer and Backtrack
and a variable explorerMode to switch between them.
Instead, whenever needed I shall call this backtrack method.
If at a junction, the robot will head back the junction as to when it first approached it.
When at a deadend or corridor, it will follow the beenbefore squares until it
reaches an unexplored path. */
public int backtrack (IRobot robot) {
if (nonwallExits(robot) > 2) {
return robotData.reverseHeading(robot);
} else {
do {
return nonWallDirections.get(0);
} while (nonwallExits(robot) == 1);
}
}
// Deadend method makes the robot follow the only nonwall exit.
public int deadEnd (IRobot robot) {
return backtrack(robot);
}
/* Corridor method will make the robot follow the one and only passage.
The exception is at the start. Sometimes, the robot will start with
two passages available to it in which case it will choose one randomly.
If there is no passage, it will follow the beenbefore squares
until it reaches an unexplored path.*/
public int corridor (IRobot robot) {
if (passageExits(robot) == 1) {
return passageDirections.get(0);
} else if (passageExits(robot) == 2) {
int randomPassage = random.nextInt(passageDirections.size());
return passageDirections.get(randomPassage);
} else {
return backtrack(robot);
}
}
/* Junction method states if there is more than one passage, it will randomly select one.
This applies to crossroads as well as essentially they are the same.
If there is no passage, it will follow the beenbefore squares until it reaches an unexplored
path. */
public int junction(IRobot robot) {
if (passageExits(robot) == 1) {
return passageDirections.get(0);
} else if (passageExits(robot) > 1) {
int randomPassage = random.nextInt(passageDirections.size());
return passageDirections.get(randomPassage);
} else {
return backtrack(robot);
}
}
// Calculates number of exits.
private int nonwallExits (IRobot robot) {
int nonwallExits = 0;
for(int item : directions) {
if(robot.look(item) != IRobot.WALL) {
nonwallExits++;
}
}
return nonwallExits;
}
// Calculates number of passages.
private int passageExits (IRobot robot) {
int passageExits = 0;
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
passageExits++;
}
}
return passageExits;
}
// Calculates number of beenbefores.
private int beenbeforeExits (IRobot robot) {
int beenbeforeExits = 0;
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
beenbeforeExits++;
}
}
return beenbeforeExits;
}
// Resets Junction Counter in RobotData class.
public int reset() {
return robotData.resetJunctionCounter();
}
}
class RobotData {
/* It was advised in the specification to include the variable:
private static int maxJunctions = 10000;
However, as I am not using arrays, but ArrayLists, I do not
need this. */
private static int junctionCounter = 0;
private ArrayList<Junction> junctionList = new ArrayList<Junction>();
private Iterator<Junction> junctionIterator = junctionList.iterator();
// Resets the Junction counter.
public int resetJunctionCounter() {
return junctionCounter = 0;
}
// Adds the current junction to the list of arrays.
public void addJunction(IRobot robot) {
Junction newJunction = new Junction(robot.getLocation().x, robot.getLocation().y, robot.getHeading());
junctionList.add(newJunction);
junctionCounter++;
}
// Gets the junction counter for Junction info method in Junction class.
public int getJunctionCounter (IRobot robot) {
return junctionCounter;
}
// Prints Junction info.
public void printJunction(IRobot robot) {
String course = "";
switch (robot.getHeading()) {
case IRobot.NORTH:
course = "NORTH";
break;
case IRobot.EAST:
course = "EAST";
break;
case IRobot.SOUTH:
course = "SOUTH";
break;
case IRobot.WEST:
course = "WEST";
break;
}
System.out.println("Junction " + junctionCounter + " (x=" + robot.getLocation().x + ", y=" + robot.getLocation().y +") heading " + course);
}
/* Iterates through the junction arrayList to find the
heading of the robot when it first approached the junction. */
public int searchJunction(IRobot robot) {
Junction currentJunction = junctionIterator.next();
while (junctionIterator.hasNext()) {
if ((((currentJunction.x)==(robot.getLocation().x))) && ((currentJunction.y)==(robot.getLocation().y)))
break;
}
return currentJunction.arrived;
}
// Returns the reverse of the heading the robot had when first approaching the junction.
public int reverseHeading(IRobot robot) {
int firstHeading = searchJunction(robot);
int reverseHeading = 1; // Random integer to Iniitalise variable.
switch (firstHeading) {
case IRobot.NORTH:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.BEHIND;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.RIGHT;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.AHEAD;
else
reverseHeading = IRobot.LEFT;
break;
case IRobot.EAST:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.LEFT;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.BEHIND;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.RIGHT;
else
reverseHeading = IRobot.AHEAD;
break;
case IRobot.SOUTH:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.AHEAD;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.LEFT;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.BEHIND;
else
reverseHeading = IRobot.RIGHT;
break;
case IRobot.WEST:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.RIGHT;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.AHEAD;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.LEFT;
else
reverseHeading = IRobot.BEHIND;
break;
}
return reverseHeading;
}
}
class Junction {
int x;
int y;
int arrived;
public Junction(int xcoord, int ycoord, int course) {
x = xcoord;
y = ycoord;
arrived = course;
}
}
Whenever it is backtracking and reaches a junction it has already visited, it freezes and this comes up.
`java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at uk.ac.warwick.dcs.maze.controllers.PolledControllerWrapper.start(PolledControllerWrapper.java:70)
at uk.ac.warwick.dcs.maze.logic.ControllerThread.run(ControllerThread.java:46)
Caused by: java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at RobotData.searchJunction(Explorer.java:242)
at RobotData.reverseHeading(Explorer.java:255)
at Explorer.backtrack(Explorer.java:74)
at Explorer.junction(Explorer.java:122)
at Explorer.controlRobot(Explorer.java:56)
... 5 more`
I don't think your searchJunction() is right or safe, the ConcurrentModificationException might be thrown due to the incorrect iterator through junctionList . The problem should be more about the iterator rather than reflection.
You might try:
private Iterator<Junction> junctionIterator = junctionList.iterator(); doesn't make much sense since the list is empty when initialize a RobotData object. Try to move it into searchJunction()
Check hasNext() first then invoke next()
public int searchJunction(IRobot robot) {
Iterator<Junction> junctionIterator = junctionList.iterator();
while (junctionIterator.hasNext()) {
Junction currentJunction = junctionIterator.next();
if ((((currentJunction.x)==(robot.getLocation().x))) && ((currentJunction.y)==(robot.getLocation().y)))
break;
}
return currentJunction.arrived;
}
It looks like that issue is in following code -
`public int searchJunction(IRobot robot) {
Junction currentJunction = junctionIterator.next();
while (junctionIterator.hasNext()) {
if ((((currentJunction.x)==(robot.getLocation().x))) && ((currentJunction.y)==(robot.getLocation().y)))
break;
}
return currentJunction.arrived;
}
You are calling junctionIterator.next() before calling junctionIterator.hasNext(). Iterator specification says that you should call next() only after calling hasNext()
Okay everybody, I know the knight's tour problem is popular for all cs students and I am having trouble getting mine to work. I use this recursive algorithm to progress through the moves, however, once I get to around move 50 I have to backtrack since no moves are available and I end up never completing the tour. I pass a ChessNode (holds things like if node has been visited, move it was visited, etc...), next row, next column, and previous node's move count.
private int moveRecur(ChessNode current, int row, int column, int moveV){
current.moveVisited = moveV+1;
if(current.moveVisited > 63){
return 0;
}
if(current.position==13 && aboard[row-1][column+2].visited != 1){
current.visited = 1;
moveRecur(aboard[row-1][column+2], row-1, column+2, current.moveVisited);
}
else if(current.position==22 && aboard[row-2][column+1].visited != 1){
current.visited = 1;
moveRecur(aboard[row-2][column+1], row-2, column+1, current.moveVisited);
}
else if(current.position == 50 && aboard[row+1][column-2].visited != 1){
current.visited = 1;
moveRecur(aboard[row+1][column-2], row+1, column-2, current.moveVisited);
}
else if(current.position == 41 && aboard[row+2][column-1].visited != 1){
current.visited =1;
moveRecur(aboard[row+2][column-1], row+2, column-1, current.moveVisited);
}
else if(current.position == 46 && aboard[row+2][column+1].visited != 1){
current.visited = 1;
moveRecur(aboard[row+2][column+1], row+2, column+1, current.moveVisited);
}
else if(current.position == 53 && aboard[row+1][column+2].visited != 1){
current.visited = 1;
moveRecur(aboard[row+1][column+2], row+1, column+2, current.moveVisited);
}
else if(current.position == 10 && aboard[row-1][column-2].visited != 1){
current.visited = 1;
moveRecur(aboard[row-1][column-2], row-1, column-2, current.moveVisited);
}
else if (current.position == 17 && aboard[row-2][column-1].visited != 1){
current.visited =1;
moveRecur(aboard[row-2][column-1], row-2, column-2, current.moveVisited);
}
if(row+1>=0 && row+1<8 && column+2>=0 && column+2<8){
if(aboard[row+1][column+2].visited != 1){
current.visited = 1;
moveRecur(aboard[row+1][column+2], row+1, column+2, current.moveVisited);
}
}
if(row+2>=0 && row+2<8 && column+1>=0 && column+1<8){
if(aboard[row+2][column+1].visited != 1){
current.visited = 1;
moveRecur(aboard[row+2][column+1], row+2, column+1, current.moveVisited);
}
}
if(row-1>=0 && row-1<8 && column-2>=0 && column-2<8){
if(aboard[row-1][column-2].visited != 1){
current.visited = 1;
moveRecur(aboard[row-1][column-2], row-1, column-2, current.moveVisited);
}
}
if(row-2>=0 && row-2<8 && column-1>=0 && column-1<8){
if(aboard[row-2][column-1].visited != 1){
current.visited = 1;
moveRecur(aboard[row-2][column-1], row-2, column-1, current.moveVisited);
}
}
if(row+1>=0 && row+1<8 && column-2>=0 && column-2<8){
if(aboard[row+1][column-2].visited != 1){
current.visited = 1;
moveRecur(aboard[row+1][column-2], row+1, column-2, current.moveVisited);
}
}
if(row+2>=0 && row+2<8 && column-1>=0 && column-1<8){
if(aboard[row+2][column-1].visited != 1){
current.visited = 1;
moveRecur(aboard[row+2][column-1], row+2, column-1, current.moveVisited);
}
}
if(row-1>=0 && row-1<8 && column+2>=0 && column+2<8){
if(aboard[row-1][column+2].visited != 1){
current.visited = 1;
moveRecur(aboard[row-1][column+2], row-1, column+2, current.moveVisited);
}
}
if(row-2>=0 && row-2<8 && column+1>=0 && column+1<8){
if(aboard[row-2][column+1].visited != 1){
current.visited = 1;
moveRecur(aboard[row-2][column+1], row-2, column+1, current.moveVisited);
}
}
//System.out.println(current.position + " "+current.moveVisited);
current.visited = 0;
return 0;
}
So, initially I check for the spots that can move to the corner board positions, and then I just make recursive calls based on available moves. So I guess my main question is am I doing something wrong? or is there another condition I can used to make the tour a little more intuitive?
Thanks in advance!
This is the Knight's tour code in java and has a brilliant layout. I did this using backtracking using recursion. This was my class assignment. Do contact me if you have any problem understanding or running this code.
package knights.tour;
import java.awt.*;
import java.awt.event.*;
import java.util.logging.*;
import javax.swing.*;
public class KnightsTour extends JFrame implements ActionListener{
//All the static variables used between action listeners and functions.
public static String path;
public static int btnPressed = 0;
public static int rowSelected;
public static String[] pathArray;
public static int[][] coordinatesArray;
public static int columnSelected;
public static int flag =0;
public static int increment = 0;
public static JPanel panel1 = new JPanel();
public static JPanel panel3 ;
public static JButton btnStart = new JButton("Start Animation");
public static JButton btnClear = new JButton("Clear");
public static JTextArea lblPath = new JTextArea();
public static JLabel lblStartRow = new JLabel();
public static JLabel lblStartColumn = new JLabel();
public static JButton[][] button;
public static int variableForIncrement=0;
static int row ;
static int column ;
static int[][] array = new int[row][column];
public static int count = 1;
KnightsTour(){
//Setting layout of the frame in the constructor and adding buttons to the panel and the frame.
getContentPane().setLayout(new GridLayout(2,1));
lblPath.setLineWrap(true);
lblPath.setColumns(10);
lblPath.setSize(700, 100);
lblPath.setEditable(false);
panel1.add(btnStart);
panel1.add(btnClear);
panel1.add(lblStartRow);
panel1.add(lblStartColumn);
panel1.add(lblPath);
panel3 = new JPanel(new GridLayout(row,column));
// Initializing Array of buttons for the user to click on the chess board.
button= new JButton[row][column];
array = new int[row][column];
coordinatesArray = new int[row*column][2]; // This array stores the coordinates as the Knight
for(int i=0;i<row;i++){
for(int j=0;j<column;j++){
button[i][j] = new JButton();
}
}
//Setting background of the buttons to black and white for chessboard layout.
for(int i=0;i<row;i++){
for(int j=0;j<column;j++){
if(i%2 ==j%2){
button[i][j].setBackground(Color.BLACK);
button[i][j].setForeground(Color.WHITE);
}
else{
button[i][j].setBackground(Color.WHITE);
}
panel3.add(button[i][j]);
button[i][j].addActionListener(this);
}
}
btnClear.addActionListener(this);
btnStart.addActionListener(this);
add(panel3);
add(panel1);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO code application logic here
String input =JOptionPane.showInputDialog("Enter the rows and columns in the format (row,column)");
String[] ar = input.split(",");
row = Integer.parseInt(ar[0]); // Finding out row and column from the user input.
column = Integer.parseInt(ar[1]);
pathArray = new String[row*column]; // This array is kept to store the path of the knight.
JFrame frame = new KnightsTour();
frame.setVisible(true);
frame.setSize(700,700);
}
//All the computation takes place in this function. It checks the neighbour and recursively calls itself.
public static void neighbourRecursion(int a,int b){
pathArray[increment] = Integer.toString(a) + "," + Integer.toString(b); // Storing the path of the Knight
increment++;
array[a][b] = count; //Stroing value of count.
button[a][b].setText(String.valueOf(count));
coordinatesArray[variableForIncrement][0] = button[a][b].getX(); //Finding coordinates of buttons to show animation
coordinatesArray[variableForIncrement][1] = button[a][b].getY();
count++;
variableForIncrement++;
//Checking for valid neighbours and calling itself recursively.
if(a <= row-3 && b<=column-2){
if(alreadyVisited(a+2,b+1)){
neighbourRecursion(a+2,b+1);
}
}
if(a<=row-3 && b>=1){
if(alreadyVisited(a+2,b-1)){
neighbourRecursion(a+2,b-1);
}
}
if(a>=2 && b<=column-2){
if(alreadyVisited(a-2,b+1)){
neighbourRecursion(a-2,b+1);
}
}
if(a>=2 && b>=1){
if(alreadyVisited(a-2,b-1)){
neighbourRecursion(a-2,b-1);
}
}
if(a<=row-2 && b>=2){
if(alreadyVisited(a+1,b-2)){
neighbourRecursion(a+1,b-2);
}
}
if(a<=row-2 && b<=column-3){
if(alreadyVisited(a+1,b+2)){
neighbourRecursion(a+1,b+2);
}
}
if(a>=1 && b>=2){
if(alreadyVisited(a-1,b-2)){
neighbourRecursion(a-1,b-2);
}
}
if(a>=1 && b <=column-3){
if(alreadyVisited(a-1,b+2)){
neighbourRecursion(a-1,b+2);
}
}
//Breaking condition of the function.
if(count == (row*column)+1){
}
// Backtracking condition if there is no neighbour.
else{
button[a][b].setText("");
array[a][b]=0;
count--;
variableForIncrement--;
if(increment >0){
increment--;
}
return ;
}
}
//This function checks if the neighbour is already visited.
public static boolean alreadyVisited(int a,int b){
if(array[a][b] != 0){
return false;
}
else{
return true;
}
}
#Override
public void actionPerformed(ActionEvent e) {
//when clear is pressed all arrays and global variables are set to initial conditon.
if(e.getSource() == btnClear){
for(int i =0;i<row;i++){
for(int j=0;j<column;j++){
array[i][j] = 0;
button[i][j].setText("");
count = 1;
lblPath.setText("");
lblStartRow.setText("");
lblStartColumn.setText("");
flag =0;
variableForIncrement=0;
increment =0;
path =" ";
}
}
}
//If start animation button is pressed animation is started.
else if(e.getSource() == btnStart){
animate();
}
// When the button is pressed.
else{
for(int i=0;i<row;i++){
for(int j=0;j<column;j++){
if(e.getSource() == button[i][j]){
if(flag == 1){
lblPath.setText(" Please press clear before clicking again"); // Button pressed twice without reset.
}
else{
rowSelected = i;
columnSelected =j;
// If odd * odd board and selected postion is odd then No path is possible.
if(row%2 ==1 && column%2 == 1 && rowSelected%2 ==0 && columnSelected%2 == 1 || row%2 ==1 && column%2 == 1 && rowSelected%2 ==1 && columnSelected%2 == 0){
lblPath.setText(" Path not possible from this point");
}
else{
int count;
lblStartRow.setText("Starting Row : "+String.valueOf(rowSelected + 1));
lblStartColumn.setText("Starting Column : "+String.valueOf(columnSelected + 1));
count = 1;
flag = 1;
startTour(); //Start tour function called.
for(int q=0;q<row;q++){
for(int w=0;w<column;w++){
if(array[i][j] == 0){
count++;
}
}
}
if(count > 2){
lblPath.setText(" No Path found");
}
//Printing path of the knight here.
else{
for(int k=0;k<pathArray.length;k++){
path = path+"->"+ pathArray[k];
}
lblPath.setText(" Path : \n"+ path.substring(5));
}
btnPressed = 1;
break;
}
}
}
}
}
} }
//Function for the animation.
void animate(){
if(btnPressed == 1){
btnPressed =0;
Graphics g = getGraphics();
for(int i=0;i<(row*column)-1;i++){
try {
Thread.sleep(600); // this function slows down drawing of lines.
} catch (InterruptedException ex) {
}
g.setColor(Color.RED); // setting colour or line to red.
g.drawLine((coordinatesArray[i][0]+65),(coordinatesArray[i][1]+50),(coordinatesArray[i+1] [0]+65),(coordinatesArray[i+1][1]+50));
}
}
else{
lblPath.setText(" Please clear, select a button to see the animation again"); //Animate button pressed twice without clear.
}
}
//This function calls the neighbour function with the selected row and column by the user.
static void startTour(){
neighbourRecursion(rowSelected,columnSelected);
for(int i=0;i<row;i++){
for(int j=0;j<column;j++){
System.out.print(array[i][j]+" ");
}
System.out.println();
}
}
}
I have an implementation of this program in C#. You can find it here:
http://github.com/danieltian/KnightBoard
It will only find the first solution though. I'm not saying to copy it, but you can take a look at it and see if it helps.