Sokoban Java Performmove error - java

This is sokoban game in java. i'm having error when i try to perform a move. Anybody can point out the changes i have to make to make it work?
Here are the errors im getting
java.lang.StringIndexOutOfBoundsException: String index out of range: 49
at java.lang.String.charAt(Unknown Source)
at State.stateString(State.java:32)
at State.(State.java:15)
at boxpushing.perform(boxpushing.java:14)
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
public class State{
private int posX;
private int posY;
private ArrayList<Integer> goalX;
private ArrayList<Integer> goalY;
private char[][] state;
public State(String s){
state=stateString(s);
goalX=new ArrayList<Integer>();
goalY=new ArrayList<Integer>();
locatePositionPlayerAndTargets();
}
public char [][] stateString(String s){
//need to put the state string into respective array position
int width = s.indexOf("\n"); //to find how many number of rows below.
int height = s.length() / width;
char newState[][] = new char[height][width];
int index = 0;
for (int i = 0; i <height; i++){
for(int j = 0; j< width; j++){
newState[i][j]=s.charAt(index);
index++;
}
index++;
}
return newState;
}
public char[][] getState(){
return state;
}
private void locatePositionPlayerAndTargets(){
//for player on target, target and crate on target
for (int i=0; i< state.length; i++){
for(int j=0; j<state[0].length; j++){
if(state[i][j] == 'P'){
posX=j;
posY=i;
} else if (state[i][j] == '+' || state[i][j] == 'o' || state[i][j] == '#' ){
goalX.add(j); //save the goal/targets values (X and Y position)
goalY.add(i);
}
}
}
}
public ArrayList<Integer> getGoalY(){
return goalY; //return the goal/target Y's position
}
public ArrayList<Integer> getGoalX(){
return goalX; //return the goal/target X's position
}
public int getPosX(){
return posX;
}
public int getPosY(){
return posY;
}
public void setChar(int y, int x, char c){
//to save the current element on that particular coordinates/position
state[y][x] = c;
}
public char getChar(int y, int x){
//will return what is on that particular coordinates/position
return state[y][x];
}
//To replace the symbol/character after player move is initiated
public void replacePlayerOnMove(int dy, int dx){
// to get the char value on the current position
char charOnPos = state[posY][posX];
switch(charOnPos){
case '+':
//if current position has 'playerOnTarget' replace with target
setChar(posY,posX, 'o');
break;
case '#':
//if current position has 'crateontarget' replace with playeronTarget
setChar(posY,posX, '+');
break;
case 'P':
//if current position has 'player' replace with background
setChar(posY,posX, '.');
break;
}
posX += dx;
posY += dy;
}
//To replace the symbol/character player is pushing the crate
public void replacePlayerOnPush(int dy, int dx){
char charOnPos = state[posY][posX];
switch(charOnPos){
case '#':
//if current position has 'crateOnTarget' replace with 'playerontarget'
setChar(posY,posX, '+');
break;
case '*':
//if current position has 'crate' replace with player
setChar(posY,posX, 'P');
break;
}
}
//To replace the symbol/character when crate is moved
public void replaceCrateOnMove(int dy, int dx, char c){
switch(c){
case 'o':
setChar(posY,posX, '#');
break;
case '.':
setChar(posY,posX, '*');
break;
}
}
public boolean checkValidMove(int dx, int dy){
try{ // need to use try and catch as this might not work / error might occur furthe debugging needed
int tmpX = getPosX()+dx;
int tmpY = getPosY()+dy;
// this is for index out of bound array checks
// then also check for crate / wall for pushing iniatiation
char newPosition = getChar(tmpY, tmpX);
if (newPosition == '*' || newPosition == '#'){
return checkValidPush(dx,dy);
} else if (newPosition == '#'){
return false;
} else {
return true;
}
} catch (ArrayIndexOutOfBoundsException e){
//if outofbound occurs just return false (possible end of boundary/map)
return false;
} catch (Exception e){
System.out.println("Error Occur tadaaaa..Yeehaa =( ");
return false;
}
}
public boolean checkValidPush(int dx, int dy){
int tmpPushX = getPosX() + dx*2; //new position of crate (x coordinates)
int tmpPushY = getPosY() + dy*2; //new position of crate (x coordinates)
char newCratePosition = getChar(tmpPushY,tmpPushX);
if (newCratePosition != '#' && newCratePosition != '*' && newCratePosition != '#'){
return true;
} else {
return false; //crate new position is invalid (possible collision might occur)
}
}
public void cratePush(int my, int mx){
//new position of crate after moved
int newCratePosX = posX+mx*2;
int newCratePosY = posY+my*2;
//get current symbol / item on the new position above
char c = getChar(newCratePosY, newCratePosX);
//replace box with the symbol / item above.
replaceCrateOnMove(newCratePosY, newCratePosX, c);
}
public void playerMove(int mx, int my){
if (checkValidMove(mx, my)){
int tmpNewX = posX+mx;
int tmpNewY = posY+my;
char newPosition = getChar(tmpNewY, tmpNewX);
switch(newPosition){
case '#':
cratePush(my,mx);
replacePlayerOnMove(my,mx);
replacePlayerOnPush(my,mx);
return;
case '*':
cratePush(my,mx);
replacePlayerOnMove(my,mx);
replacePlayerOnPush(my,mx);
return;
case '.':
setChar(tmpNewY,tmpNewX,'P');
replacePlayerOnMove(my,mx);
return;
case 'o':
setChar(tmpNewY,tmpNewX,'+');
replacePlayerOnMove(my,mx);
return;
default:
System.out.println("The move is not valid");
return;
}
}
}
public String toString(){
String printString = "";
for (int i=0; i<state.length; i++){
for (int j=0; j<state[0].length; j++){
printString += state[i][j];
}
printString += "\n";
}
return printString;
}
}
Here are the boxpushing codes
public class boxpushing {
private final static char player = 'P';
private final static char playerOnTarget = '+';
private final static char wall = '#';
private final static char crate = '*';
private final static char crateOnTarget= '#';
private final static char target = 'o';
private final static char background = '.';
private State currentState;
public String perform(String state, char move){
currentState = new State(state);
performMove(move);
System.out.println("Before");
System.out.println(state);
System.out.println("");
return currentState.toString();
}
public void performMove(char moveInput){
switch (moveInput){
case 'u':
currentState.playerMove(0, 1);
break;
case 'd':
currentState.playerMove(0, -1);
break;
case 'l':
currentState.playerMove(-1, 0);
break;
case 'r':
currentState.playerMove(1, 0);
break;
}
}
}

Hint: One of the
index++;
is superfluous.
Figuring out which one is left as an exercise for the reader.

Related

How to place a number in every position my horse pawn has been to (in a 2D "board" array)

The program basically prompts the user to pick one of the displayed moves and move the horse pawn in the chess board. Ιn this way we try to see how many times the horse can move to the chessboard without being out of bounds or even in the same position.
The problem is that I haven't figured out a way to place a number (starting from 1) for the positions where the horse has been to. More specifically I want it to print H in the current position of the horse and 1 to ... on the past positions.
import java.util.Scanner;
public class Ex7_22 {
private static Scanner scanner = new Scanner(System.in);
private static String[][] board = new String[8][8];
private static int[] horizontal = new int[8];
private static int[] vertical = new int[8];
private static boolean[][] boardPositions = new boolean[8][8];
private static int currentRow = 3;
private static int currentColumn = 4;
public static void main(String[] args){
fillArrays();
boolean outOfBoundsV;
boolean outOfBoundsH;
boolean positionAvailability = false;
int VerticalMove,HorizontalMove;
fillBoard();
board[3][4] = "H";
boardPositions[3][4] = true;
displayBoard();
int pickMove;
for(int i=1;i<=64;i++){
displayBoardPositions();
displayPossibleMoves();
do {
System.out.println("\nPick one of the displayed moves to do (0-7): ");
pickMove = scanner.nextInt();
VerticalMove = moveHorse_Vertical(pickMove);
HorizontalMove = moveHorse_Horizontal(pickMove);
outOfBoundsV = checkForOutOfBounds(VerticalMove);
outOfBoundsH = checkForOutOfBounds(HorizontalMove);
if ((outOfBoundsV)||(outOfBoundsH)){
reverse(pickMove);
}
if((!outOfBoundsV)&&(!outOfBoundsH)){
positionAvailability = checkForPositionAvailability(VerticalMove,HorizontalMove);
if((!positionAvailability)){
reverse(pickMove);
}
}
}while (((outOfBoundsV)||(outOfBoundsH))||(!positionAvailability));
board[VerticalMove][HorizontalMove] = "H";
//placeNumber(VerticalMove,HorizontalMove,pickMove);
boardPositions[VerticalMove][HorizontalMove] = true;
displayBoard();
}
}
/* private static void placeNumber(int VerticalMove, int HorizontalMove, int pickMove) {
//have to place number in every position the horse has been to. Haven't managed to get it working.
reverse(pickMove);
int i=0;
i++;
String iStr = String.valueOf(i);
board[VerticalMove][HorizontalMove] = iStr;
currentRow += vertical[pickMove];
currentColumn += horizontal[pickMove];
} */
private static void reverse(int move){
currentRow -= vertical[move];
currentColumn -= horizontal[move];
}
private static void fillBoard(){
for(int i=0;i<board.length;i++){
for(int j=0;j<board[i].length;j++){
board[i][j] = "0";
}
}
}
private static boolean checkForPositionAvailability(int vertical, int
horizontal) {
if(!boardPositions[vertical][horizontal]){
return true;
}
else{
System.out.println("You've already been in this position.");
return false;
}
}
private static boolean checkForOutOfBounds(int position) {
if((position<0)||(position>=8)){
System.out.print("Position out of bounds\nPlease choose another move\n");
return true;
}
else return false;
}
private static int moveHorse_Vertical(int move) {
currentRow += vertical[move];
return currentRow;
}
private static int moveHorse_Horizontal(int move){
currentColumn += horizontal[move];
return currentColumn;
}
private static void fillArrays() {
//fill horizontal array
horizontal[0] = 2;
horizontal[1] = 1;
horizontal[2] = -1;
horizontal[3] = -2;
horizontal[4] = -2;
horizontal[5] = -1;
horizontal[6] = 1;
horizontal[7] = 2;
//fill vertical array
vertical[0] = -1;
vertical[1] = -2;
vertical[2] = -2;
vertical[3] = -1;
vertical[4] = 1;
vertical[5] = 2;
vertical[6] = 2;
vertical[7] = 1;
}
private static void displayBoard(){
System.out.print("\n");
System.out.println(" 0 1 2 3 4 5 6 7");
System.out.println(" ----------------------------- ");
for(int i=0;i<board.length;i++){
System.out.print(i + "|\t");
for(int j=0;j<board[i].length;j++){
System.out.print(" "+board[i][j] + "\t");
}
System.out.println();
}
}
private static void displayBoardPositions(){
System.out.print("\n");
System.out.println(" 0 1 2 3 4 5 6 7");
System.out.println(" ----------------------------- ");
for(int i=0;i<boardPositions.length;i++){
System.out.print(i + "|\t");
for(int j=0;j<boardPositions[i].length;j++){
System.out.print(" "+boardPositions[i][j] + "\t");
}
System.out.println();
}
}
private static void displayPossibleMoves(){
System.out.println("\n0 -> 1 move up // 2 moves right.\n" +
"1 -> 2 moves up // 1 move right.\n" +
"2 -> 2 moves up // 1 move left.\n" +
"3 -> 1 move up // 2 moves left.\n" +
"4 -> 1 move down // 2 moves left.\n" +
"5 -> 1 move left // 2 moves down.\n" +
"6 -> 1 move right // 2 moves down.\n" +
"7 -> 1 move down // 2 moves right.");
}
}
Hey i don't think you need to change much. You just need to add two integers that keep track of your previous position. I tried to keep the modifications of your code to a minimum:
int previousRow = 3;
int previousColumn = 4;
and update them after you set the current position to "H":
Here is a modified version of your public static main(String[] args):
public static void main(String[] args){
fillArrays();
boolean outOfBoundsV;
boolean outOfBoundsH;
boolean positionAvailability = false;
int VerticalMove,HorizontalMove;
fillBoard();
board[3][4] = "H";
boardPositions[3][4] = true;
// add the two integers that keep track of you previous position:
int previousRow = 3;
int previousColumn = 4;
displayBoard();
int pickMove;
for(int i=1;i<=64;i++){
displayBoardPositions();
displayPossibleMoves();
do {
System.out.println("\nPick one of the displayed moves to do (0-7): ");
pickMove = scanner.nextInt();
VerticalMove = moveHorse_Vertical(pickMove);
HorizontalMove = moveHorse_Horizontal(pickMove);
outOfBoundsV = checkForOutOfBounds(VerticalMove);
outOfBoundsH = checkForOutOfBounds(HorizontalMove);
if ((outOfBoundsV)||(outOfBoundsH)){
reverse(pickMove);
}
if((!outOfBoundsV)&&(!outOfBoundsH)){
positionAvailability = checkForPositionAvailability(VerticalMove,HorizontalMove);
if((!positionAvailability)){
reverse(pickMove);
}
}
} while ((outOfBoundsV)||(outOfBoundsH)||(!positionAvailability));
// set the previous position to your current step of i:
board[previousRow][previousColumn] = String.valueOf(i);
board[VerticalMove][HorizontalMove] = "H";
//update your previous position, with your current position
previousColumn = currentColumn;
previousRow = currentRow;
boardPositions[VerticalMove][HorizontalMove] = true;
displayBoard();
}
}

match and delete elements in arraylists

I am making a robot maze where the robot reaches a target automatically without crashing into walls. I want the robot to do the maze once, learn the correct route and then the second time be able to get there straight away without going to any deadends. I thought I could do this by making three arraylists.
One for all the squares the robot visits.
Two for all the squares that lead to a deadend.
Three for all the directions the robot goes.
If the squares that lead to a dead end are found in the first arraylist then i can delete the same indexes in the third arraylist. That way, the second time, i can just iterate the third Arraylist.
My full code is below:
import java.util.ArrayList;
import java.util.*;
import java.util.Iterator;
import java.util.stream.IntStream;
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};
private ArrayList<Square> correctSquares;
private ArrayList<Square> wrongSquares;
private ArrayList<Integer> correctDirections;
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;
if ((robot.getRuns() != 0))
direction = grandfinale(robot);
nonWallDirections = new ArrayList<Integer>();
passageDirections = new ArrayList<Integer>();
beenbeforeDirections = new ArrayList<Integer>();
correctSquares = new ArrayList<Square>();
correctDirections = 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);
addcorrectSquares(robot);
correctDirections.add(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) {
addwrongSquares(robot);
return robotData.reverseHeading(robot);
} else {
do {
addwrongSquares(robot);
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();
}
public void addcorrectSquares(IRobot robot) {
Square newSquare = new Square(robot.getLocation().x, robot.getLocation().y);
correctSquares.add(newSquare);
}
public void addwrongSquares(IRobot robot) {
Square badSquare = new Square(robot.getLocation().x, robot.getLocation().y);
wrongSquares.add(badSquare);
}
public int grandfinale (IRobot robot) {
IntStream.range(0, correctSquares.size())
.map(index -> correctSquares.size() - index - 1)
.filter(index -> (((wrongSquares.x).contains(correctSquares.x)) && ((wrongSquares.y).contains(correctSquares.y))).get(index))
.forEach(index -> correctDirections.remove(index));
Iterator<Integer> routeIterator = correctDirections.iterator();
while (routeIterator.hasNext()) {
break;
}
return (routeIterator.next());
}
}
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>();
// 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.
It does this by finding the first junction in the ArrayList
that has the same x and y coordinates as the robot.*/
public int searchJunction(IRobot robot) {
Junction currentJunction = null;
Iterator<Junction> junctionIterator = junctionList.iterator();
while (junctionIterator.hasNext()) {
currentJunction = junctionIterator.next();
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;
}
}
class Square {
int x;
int y;
public Square(int cordx, int cordy){
x = cordx;
y = cordy;
}
}
IntStream.range(0, al1.length)
.filter(index -> al2.contains(al1.get(index)))
.forEach(index -> al3.remove(index));
Slightly more complex than this if removing elements from al3 shifts them left but in that case just reverse the stream before the .filter- then it will delete from the end. The easiest way to do that is:
.map(index -> al1.length - index - 1)
Without Streams the equivalent would be
for (int i = 0; i < al1.length; i++) {
if (al2.contains(al1.get(i))) {
al3.remove(i);
}
}
Similarly, if you need to delete from the right then the for loop would need to count down rather than up.
Without further details on arraylist structure it's hard to give any more hints.

Text file load error and array error on mouse island application

I have a problem, I am not looking for answers to my problem I would like some help finding why my array even though specified in main unders switch: case1, case2, case3. I used a for loops with an array that stops at the 5th iteration. However when I run the program it only runs once, am I specifying correctly to make it run 5 times or should it be declared another way? thanks in advance. I should also include there are no errors reported by eclipse at this time until it is ran and only after the first input.
The text files contains
##B##
#---#
#-M-#
#---#
##B##
##B##########
#-----------#
#-----------#
#-----------B
#-----------#
#------M----#
#-----------#
#-----------#
#-----------#
#-----------#
#-----------#
#-----------#
#############
##B#####
#------#
#-M----#
#------#
#------#
#------#
#------#
#####B##
The island maps can be found here
[http://rapidshare.com/share/9704FE33EFF98F1C1E71F6F1DF2DC0D4]
This is the array (int i=0;i<5;i++) however I do not think this is the problem, I can also provide the text files if needed
This is the console out
CS1181 Mouse Island
1. mouseIsland1.txt
2. mouseIsland2.txt
3. mouseIsland3.txt
9. Exit
Please make your selection: 2
Filename: mouseIsland2.txt
Bridge1: 0,0
Bridge2: 0,0
Mouse: 0,0
OUCH! The Mouse fell into the water and died at: 1|1
01
0100000000000
0000000000000
0000000000000
0000000000000
0000000000000
0000000000000
0000000000000
0000000000000
0000000000000
0000000000000
0000000000000
0000000000000
0000000000000
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
at MouseEscape.runMouseIsland(MouseEscape.java:349)
at MouseEscape.main(MouseEscape.java:71)
End console
import java.io.File;
import java.util.Scanner;
public class MouseEscape {
public static Scanner input = new Scanner(System.in);
public static MouseEscape island1;
public static MouseEscape island2;
public static MouseEscape island3;
private String islandTxt;
private boolean moveDebug;
private int mouseEscaped;
private int mouseDrowned;
private int mouseStarved;
private int islandRows;
private int [] islandCols;
private int runCount;
private int [][] mousePosition;
private int [][] bridgePosition;
private int [][] islandIntArray;
private char [][] islandCharArray;
// main
// Allows the user to select which mouse island map to simulate
public static void main(String[] args) throws Exception
{
System.out.println("CS1181 Mouse Island");
int choice = 0, continueRun = 1;
boolean runResponce = false, correctInput = false;
while (continueRun == 1)
{
System.out.print("\n 1. mouseIsland1.txt"
+ "\n 2. mouseIsland2.txt"
+ "\n 3. mouseIsland3.txt"
+ "\n 9. Exit\n\nPlease make your selection: ");
continueRun = 9;
runResponce = false;
while (correctInput == false){
while (!input.hasNextInt()) {
input.next();
System.out.print("Enter a number 1-3 or 9 to exit.\nPlease make your selection: ");
}
choice = input.nextInt();
if (choice>=1 && choice <=3 || choice == 9){
correctInput = true;
break;
}
}
switch(choice)
{
case 1:
MouseEscape island1 = new MouseEscape("mouseIsland1.txt");
System.out.println("\nFilename: "+island1.getIslandTxt()
+"\nBridge1: "+island1.getBridgePosition(0,0)+","+island1.getBridgePosition(0,1)
+"\nBridge2: "+island1.getBridgePosition(1,0)+","+island1.getBridgePosition(1,1)
+"\nMouse: "+island1.getMousePosition(1,0)+","+island1.getMousePosition(1,1)+"\n");
//island1.drawCharIsland();
for (int i=0;i<5;i++) island1.runMouseIsland();
island1.printIslandStats();
correctInput=false; continueRun=1; break;
case 2:
MouseEscape island2 = new MouseEscape("mouseIsland2.txt");
System.out.println("\nFilename: "+island2.getIslandTxt()
+"\nBridge1: "+island2.getBridgePosition(0,0)+","+island2.getBridgePosition(0,1)
+"\nBridge2: "+island2.getBridgePosition(1,0)+","+island2.getBridgePosition(1,1)
+"\nMouse: "+island2.getMousePosition(1,0)+","+island2.getMousePosition(1,1)+"\n");
//island1.drawCharIsland();
for (int i=0;i<5;i++) island2.runMouseIsland();
island2.printIslandStats();
correctInput=false; continueRun=1; break;
case 3:
MouseEscape island3 = new MouseEscape("mouseIsland3.txt");
System.out.println("\nFilename: "+island3.getIslandTxt()
+"\nBridge1: "+island3.getBridgePosition(0,0)+","+island3.getBridgePosition(0,1)
+"\nBridge2: "+island3.getBridgePosition(1,0)+","+island3.getBridgePosition(1,1)
+"\nMouse: "+island3.getMousePosition(1,0)+","+island3.getMousePosition(1,1)+"\n");
//island1.drawCharIsland();
for (int i=0;i<5;i++) island3.runMouseIsland();
island3.printIslandStats();
correctInput=false; continueRun=1; break;
}
if (runResponce == false)
{
if (continueRun == 1)
{
runResponce = true;
correctInput = false;
}
}
}
input.close();
}
// MouseIslandClass
// Constructs a mouseIslandClass without specifying which mouseIsland to load
public MouseEscape() {
islandTxt = "";
mouseEscaped = 0;
mouseDrowned = 0;
mouseStarved = 0;
islandRows = 0;
runCount = 0;
mousePosition = null;
bridgePosition = null;
islandIntArray = null;
islandCharArray = null;
}
// MouseIslandClass
// Constructs a mouseIslandClass given a mouseIsland map name
public MouseEscape(String _islandTxt) throws Exception{
islandTxt = _islandTxt;
loadIsland();
}
// setIslandTxt
// Sets the mouseIsland filename for the current mouseIsland
public void setIslandTxt(String _islandTxt) throws Exception{
islandTxt = _islandTxt;
}
// getIslandTxt
// Gets the mouseIsland filename for the current mouseIsland
public String getIslandTxt(){
return islandTxt;
}
// getMouseEscaped
// Returns the total number of times a mouse has escaped from the current mouseIsland
public int getMouseEscaped(){
return mouseEscaped;
}
// getMouseDrowned
// Returns the total number of times a mouse has drowned on the current mouseIsland
public int getMouseDrowned(){
return mouseDrowned;
}
// getMouseStarved
// Returns the total number of times a mouse has starved on the current mouseIsland
public int getMouseStarved(){
return mouseStarved;
}
// getBridgePosition
// Returns the coordinate row(x) or column(y) to either of the bridges on the current mouseIsland
public int getBridgePosition(int x, int y){
return bridgePosition[x][y];
}
// getMousePosition
// Returns the coordinate row(x) or column(y) of the mouse on the current mouseIsland
public int getMousePosition(int x, int y){
return mousePosition[x][y];
}
// loadIsland
// Populates any information needed to run the simulation for the current mouseIsland
public void loadIsland() throws Exception{
if (islandTxt == "" || islandTxt == null){
System.out.println("loadIsland() failed! 'islandTxt' variable is empty!");
return;
}
findIslandRow();
findIslandCol();
setCharIslandArray();
findIslandVariables();
}
// printIslandStats
// Prints to the console the statistics for this mouseIsland at its current state
public void printIslandStats(){
System.out.println("Run count: " + runCount + " times\n"
+ "Drowned: " + mouseDrowned + " times\n"
+ "Starved: " + mouseStarved + " times\n"
+ "Escaped: " + mouseEscaped + " times \n");
}
// maxValue
// This function returns the max value of an integer array.
public int maxValue(int [] inArray){
int value = 0;
for (int i=0;i<inArray.length;i++)
if (value<inArray[i]) value = inArray[i];
return value;
}
// findIslandRow
// Counts the number of rows for the current mouseIsland
public void findIslandRow() throws Exception {
Scanner input = new Scanner(new File(islandTxt));
islandRows = 0;
while(input.hasNext()){
input.nextLine();
islandRows++;
}
//System.out.println("Rows: "+islandRows);
input.close();
}
// findIslandCol
// Counts and stores the number of columns for each row in the current mouseIsland
public void findIslandCol() throws Exception {
Scanner input = new Scanner(new File(islandTxt));
String inputLine = ""; int row = 0; islandCols = new int [islandRows];
while(input.hasNext()){
inputLine = input.nextLine();
islandCols[row] = inputLine.length();
//System.out.println("Col"+row+": "+islandCols[row]);
row++;
}
input.close();
}
// loads a mouse island map into a 2 dimensional character array
public void setCharIslandArray() throws Exception {
Scanner input = new Scanner(new File(islandTxt));
islandCharArray = new char [islandRows+1][maxValue(islandCols)+1];
String islandRow ="";
for(int row=0;row<islandRows;row++){
islandRow = input.nextLine();
for (int col=0;col<islandRow.length();col++) {
islandCharArray[row][col] = islandRow.charAt(col);
}
}
input.close();
}
// drawCharIsland
// Draws a character array to the console for testing
public void drawCharIsland() throws Exception{
String ln = "";
for (int row= 0;row<islandRows;row++){
for (int col= 0;col<islandCols[row];col++){
if (col == islandCols[row]-1) ln = "\n"; else ln ="";
System.out.print(islandCharArray[row][col]+ln);
}
}
System.out.println("");
}
// drawIntIsland
// Draws an integer array to the console for testing
public void drawIntIsland() throws Exception{
String ln = "";
for (int row= 0;row<islandRows;row++){
for (int col= 0;col<islandCols[row];col++){
if (col == islandCols[row]-1) ln = "\n"; else ln ="";
System.out.print(islandIntArray[row][col]+ln);
}
}
System.out.println("");
}
// drawBigIntIsland
// Draws an integer array with special formatting for larger numbers the console for testing
public void drawBigIntIsland() throws Exception{
String ln = ""; String rowZero = ""; String colZero = "";
int i=0;
for (int row= 0;row<islandRows;row++){
if (row <= 9) rowZero = " "; else rowZero ="";
for (int col= 0;col<islandCols[row];col++){
if (row == 0)
while (i<islandRows){
if (i == 0) System.out.print("XY");
if (i <= 9) colZero = " "; else colZero ="";
if (i == islandCols[row]-1) ln = "\n"; else ln ="";
System.out.print(colZero+i+ln);
i++;
}
if (col == islandCols[row]-1) ln = "\n"; else ln ="";
if (islandIntArray[row][col] <= 9) colZero = "|"; else colZero ="";
if (col == 0) System.out.print(rowZero+row);
if (row >=0 && col >=0) System.out.print(colZero+islandIntArray[row][col]+ln);
}
}
}
// findIslandVariables
// finds and stores all of the mouseIsland object variables
public void findIslandVariables() throws Exception{
int bCount = 0;
mousePosition = new int [2][2]; bridgePosition = new int [200][2];
for (int row= 0;row<islandRows;row++){
for (int col= 0;col<islandCols[row];col++){
//System.out.println(row+"|"+col);
switch(islandCharArray[row][col]) {
case 'X' : mousePosition[0][0] = row; mousePosition[0][1] = col; //current position
mousePosition[1][0] = row; mousePosition[1][1] = col; //start position
//System.out.println("Mouse found on: "+row+"|"+col);
break;
case '-' :
if (row == 0 || col == 0 || row == islandRows-1 || col == islandCols[row]-1){
bridgePosition[bCount][0] = row; bridgePosition[bCount][1] = col;
bCount++;
//System.out.println("Bridge"+bCount+": "+row+"|"+col);
} else if (col>=islandCols[row-1]-1 || col>=islandCols[row+1]-1){
bridgePosition[bCount][0] = row; bridgePosition[bCount][1] = col;
//System.out.println("Bridge found: "+row+"|"+col);
bCount++;
}
break;
}
}
}
}
// moveMouse
// Computes the movement for the mouse
// set moveDebug to 'true' to display the mouse's moves
public void moveMouse(){
moveDebug = false;
int mouseMove = (int)(Math.random() * 4);
switch(mouseMove){
case 0: mousePosition[0][0]--; if (moveDebug == true) System.out.print("Move: "+mouseMove+"[UP] "); break;
case 1: mousePosition[0][0]++; if (moveDebug == true) System.out.print("Move: "+mouseMove+"[DOWN] "); break;
case 2: mousePosition[0][1]--; if (moveDebug == true) System.out.print("Move: "+mouseMove+"[LEFT] "); break;
case 3: mousePosition[0][1]++; if (moveDebug == true) System.out.print("Move: "+mouseMove+"[RIGHT] "); break;
}
if (moveDebug == true) System.out.println(" Location:|"+mousePosition[0][0]+"|"+mousePosition[0][1]+"|");
}
// runMouseIsland
// Displays the outcome of one trial of the current mouseIsland
public void runMouseIsland() throws Exception{
islandIntArray = new int [islandRows][maxValue(islandCols)];
mousePosition[0][0] = mousePosition [1][0]; mousePosition[0][1] = mousePosition [1][1];
for (int count=0;count<100;count++){
moveMouse();
if (mousePosition[0][0] == bridgePosition[0][0] && mousePosition[0][1] == bridgePosition[0][1] || mousePosition[0][0] == bridgePosition[1][0] && mousePosition[0][1] == bridgePosition[1][1] ){
System.out.println("The mouse has escaped using the bridge at: "+mousePosition[0][0]+"|"+mousePosition[0][1]);
islandIntArray[mousePosition[0][0]][mousePosition[0][1]]++;
mouseEscaped++;
break;
} else
if (islandCharArray[mousePosition[0][0]][mousePosition[0][1]] == '#') {
System.out.println("OUCH! The Mouse fell into the water and died at: "+mousePosition[0][0]+"|"+mousePosition[0][1]);
islandIntArray[mousePosition[0][0]][mousePosition[0][1]]++;
mouseDrowned++;
break;
}
islandIntArray[mousePosition[0][0]][mousePosition[0][1]]++;
if (count == 99){
System.out.println("The mouse withered away (died) at: "+mousePosition[0][0]+"|"+mousePosition[0][1]);
mouseStarved++;
}
}
drawIntIsland();
runCount++;
}
}

my 2D game, Sokoban, is working, but I want to know if I improve the design [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I've made a Sokoban with two classes, logic and SokobanGUI.
Basically, the logicpart builds an ArrayList> from an input String Array, f.ex:
String[] level1 =
{"*###########*",
"# #",
"# ... ... #",
"# *$$ $.$ #",
"# $*+$ $*$$ #",
"# *$$ $.$ #",
"# ... . #",
"# #",
"*###########*"};
where '+' is the player position.
Anyway, the ArrayList is updated in the logic class as the game is played (player is moving, characters are interacting etc.)
The graphics part of the game is solved by assigning pictures to each character and looping through the arraylist>. Then this is updated inn an infinite while-loop such that when the arraylist> is updated in the logic part, it is printed again.
If this was difficult to follow, I've printed the code below.
Question1: I don't think this is the best way to output the game (with a while-lopp) as I am graphically outputting the game all the time, even when no changes have been made. How is this usually dealt with? Is Swing something that can help med with this?
Question2: Also, in the code below, I don't use the main method, I use the run(). The main method didn't like me making non-statical references, and run worked so I went with it... How am I supposed to use the main method to make the game work?
Maybe it is easier to see my code:
Logic class
package Sokoban2;
import java.util.ArrayList;
public class Logic {
int player_x;
int player_y;
int nextMove_x;
int nextMove_y;
ArrayList<ArrayList<Boolean>> targets;
ArrayList<ArrayList<Character>> grid;
Boolean canMove;
public Logic(){
buildGrid();
buildTargets();
findPlayer();
}
public void move(int dx, int dy){
if(CheckCanMove(dx, dy)){
moveBox(dx, dy);
movePlayer(dx, dy);
}
}
public void movePlayer(int dx, int dy){
boolean boxAhead = (getChar(dx,dy) == '#');
if(!boxAhead){
char pChar = grid.get(player_x ).get(player_y);
char pCharNext = grid.get(player_x +dx).get(player_y +dy);
if(pChar == '#'){
if(pCharNext == ' ' || pCharNext == '$'){
grid.get(player_x + dx).set(player_y + dy, '#');
grid.get(player_x).set(player_y, ' ');
}
if(pCharNext == '.' || pCharNext == '*'){
grid.get(player_x + dx).set(player_y + dy, '+');
if(pChar == '+'){
grid.get(player_x).set(player_y, '.');
}
else{
grid.get(player_x).set(player_y, ' ');
}
}
}
else if(pChar == '+'){
if(pCharNext == ' ' || pCharNext == '$'){
grid.get(player_x + dx).set(player_y + dy, '#');
grid.get(player_x).set(player_y, '.');
}
if(pCharNext == '.' || pCharNext == '*'){
grid.get(player_x + dx).set(player_y + dy, '+');
grid.get(player_x).set(player_y, '.');
}
}
player_x += dx;
player_y += dy;
}
}
public void buildGrid(){
char c = '"';
grid = new ArrayList<ArrayList<Character>>();
String[] level1 =
{"*###########*",
"# #",
"# ... ... #",
"# *$$ $.$ #",
"# $*+$ $*$$ #",
"# *$$ $.$ #",
"# ... . #",
"# #",
"*###########*"};
int length_i = level1.length;
System.out.println(length_i);
int length_i_j = level1[length_i-1].length();
for(int i = 0; length_i>i; i++){
grid.add(i, new ArrayList<Character>());
for(int j = 0; length_i_j>j; j++){
c = level1[i].charAt(j);
grid.get(i).add(c);
}
}
}
public void buildTargets(){
targets = new ArrayList<ArrayList<Boolean>>();
char c = ' ';
int length_i = grid.size();
for(int i = 0; length_i>i; i++){
targets.add(i, new ArrayList<Boolean>());
for(int j = 0; grid.get(i).size()>j; j++){
c = grid.get(i).get(j);
if(c == '.'){
targets.get(i).add(false);
}
else{
targets.get(i).add(true);
}
}
}
}
public void findPlayer(){
char c = ' ';
int length_i = grid.size();
for(int i = 0; length_i>i; i++){
for(int j = 0; grid.get(i).size()>j; j++){
c = grid.get(i).get(j);
if(c == '#' || c == '+'){
player_x = i;
player_y = j;
}
}
}
}
public Character getChar(int dx, int dy){
char c = ' ';
c = grid.get(player_x + dx).get(player_y + dy);
return c;
}
public void moveBox(int dx, int dy){
char c1 = getChar((dx),(dy));
char c2 = getChar((2*dx),(2*dy));
if(c1 == '*' || c1 == '$'){
if(c2 == '.'){
grid.get(player_x +2*dx).set(player_y +2*dy, '*');
}
if(c2 == ' '){
grid.get(player_x +2*dx).set(player_y +2*dy, '$');
}
}
}
public boolean CheckCanMove(int dx, int dy){
canMove = true;
boolean boxAhead = (getChar(dx,dy) == '#');
boolean boxAhead2 = (getChar(2*dx,2*dy) == '#');
boolean movableAhead = (getChar(dx,dy) == '$');
boolean movableAhead2 = (getChar(2*dx,2*dy) == '$');
boolean movableAheadS = (getChar(dx,dy) == '*');
boolean movableAheadS2 = (getChar(2*dx,2*dy) == '*');
if(boxAhead || (movableAhead && boxAhead2) || (movableAhead &&
movableAhead2) || (movableAheadS && movableAheadS2) || (movableAheadS &&
boxAhead2)){
canMove = false;
}
return canMove;
}
void setPlayerX(int player_x){
this.player_x = player_x;
}
void setPlayerY(int player_y){
this.player_y = player_y;
}
}
Graphics Class
package Sokoban2;
import acm.graphics.GImage;
import java.awt.event.KeyEvent;
import acm.graphics.GImage;
import acm.program.GraphicsProgram;
public class SokobanGUI extends GraphicsProgram {
Logic logic = new Logic();
GImage wall;
GImage blank;
#Override
public void init() {
logic = new Logic();
addKeyListeners(); // This is required for listening to key events
}
#Override
public void run() {
System.out.println(logic.player_x);
System.out.println(logic.player_y);
while(true){
paint();
println("hah");
}
// System.out.println("win");
}
public void paint(){
char c =' ';
int length_i = logic.grid.size();
for(int i = 0; length_i>i; i++){
for(int j = 0; logic.grid.get(i).size()>j; j++){
c = logic.grid.get(i).get(j);
switch (c) {
case '#':
GImage wall = new GImage("sokoban/wall16x16.png");
wall.setLocation(16*j,16*i);
add(wall);
break;
case ' ':
GImage blank = new GImage("sokoban/blank16x16.png");
blank.setLocation(16*j,16*i);
add(blank);
break;
case '#':
GImage player = new GImage("sokoban/mover16x16.png");
player.setLocation(16*j,16*i);
add(player);
break;
case '$':
GImage movable = new GImage("sokoban/movable16x16.png");
movable.setLocation(16*j,16*i);
add(movable);
break;
case '.':
GImage target = new GImage("sokoban/target16x16.png");
target.setLocation(16*j,16*i);
add(target);
break;
case '*':
GImage movable_on_target = new
GImage("sokoban/movable_on_target16x16.png");
movable_on_target.setLocation(16*j,16*i);
add(movable_on_target);
break;
case '+':
GImage mover_on_target = new
GImage("sokoban/mover_on_target16x16.png");
mover_on_target.setLocation(16*j,16*i);
add(mover_on_target);
break;
}
}
}
}
public boolean win(){
int length_i = logic.targets.size();
int win = 0;
for(int i = 0; length_i>i; i++){
for(int j = 0; logic.targets.get(i).size()>j; j++){
if(!logic.targets.get(i).get(j)){
win += 1;
}
}
}
return (!(win>0));
}
#Override
public void keyPressed(KeyEvent event) {
int key;
if (Character.isLetter(event.getKeyChar()))
key = event.getKeyChar();
else
key = event.getKeyCode();
switch (key) {
case 'w':
logic.move(-1, 0);
break;
case 's':
logic.move(1, 0);
break;
case 'a':
logic.move(0, -1);
break;
case 'd':
logic.move(0, 1);
break;
}
}
}
Question 1: To prevent having to continually paint, even with no input, just check to see if anything has changed (monsters moved, player moved, etc.). I assume this is a Roguelike, so you probably only need to check and see if the player moved.
if( playerMoved() ) {
paint();
}
Question 2: I don't know what GraphicsProgram contains, but I assume the main() within it is being used. There is nothing inherently wrong with using the run() method to run your game loop and, in fact, this is how most games work. At most, your main() would just be kicking off the run() method, as it is now. So you're good!
HTH

Time delay and JInput

OK, I don't know how to word this question, but maybe my code will spell out the problem:
public class ControllerTest
{
public static void main(String [] args)
{
GamePadController rockbandDrum = new GamePadController();
DrumMachine drum = new DrumMachine();
while(true)
{
try{
rockbandDrum.poll();
if(rockbandDrum.isButtonPressed(1)) //BLUE PAD HhiHat)
{
drum.playSound("hiHat.wav");
Thread.sleep(50);
}
if(rockbandDrum.isButtonPressed(2)) //GREEN PAD (Crash)
{
//Todo: Change to Crash
drum.playSound("hiHat.wav");
Thread.sleep(50);
}
//Etc....
}
}
}
public class DrumMachine
{
InputStream soundPlayer = null;
AudioStream audio = null;
static boolean running = true;
public void playSound(String soundFile)
{
//Tak a sound file as a paramater and then
//play that sound file
try{
soundPlayer = new FileInputStream(soundFile);
audio = new AudioStream(soundPlayer);
}
catch(FileNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
AudioPlayer.player.start(audio);
}
//Etc... Methods for multiple audio clip playing
}
Now the problem is, if I lower the delay in the
Thread.sleep(50)
then the sound plays multiple times a second, but if I keep at this level or any higher, I could miss sounds being played...
It's an odd problem, where if the delay is too low, the sound loops. But if it's too high it misses playing sounds. Is this just a problem where I would need to tweak the settings, or is there any other way to poll the controller without looping sound?
Edit: If I need to post the code for polling the controller I will...
import java.io.*;
import net.java.games.input.*;
import net.java.games.input.Component.POV;
public class GamePadController
{
public static final int NUM_BUTTONS = 13;
// public stick and hat compass positions
public static final int NUM_COMPASS_DIRS = 9;
public static final int NW = 0;
public static final int NORTH = 1;
public static final int NE = 2;
public static final int WEST = 3;
public static final int NONE = 4; // default value
public static final int EAST = 5;
public static final int SW = 6;
public static final int SOUTH = 7;
public static final int SE = 8;
private Controller controller;
private Component[] comps; // holds the components
// comps[] indices for specific components
private int xAxisIdx, yAxisIdx, zAxisIdx, rzAxisIdx;
// indices for the analog sticks axes
private int povIdx; // index for the POV hat
private int buttonsIdx[]; // indices for the buttons
private Rumbler[] rumblers;
private int rumblerIdx; // index for the rumbler being used
private boolean rumblerOn = false; // whether rumbler is on or off
public GamePadController()
{
// get the controllers
ControllerEnvironment ce =
ControllerEnvironment.getDefaultEnvironment();
Controller[] cs = ce.getControllers();
if (cs.length == 0) {
System.out.println("No controllers found");
System.exit(0);
}
else
System.out.println("Num. controllers: " + cs.length);
// get the game pad controller
controller = findGamePad(cs);
System.out.println("Game controller: " +
controller.getName() + ", " +
controller.getType());
// collect indices for the required game pad components
findCompIndices(controller);
findRumblers(controller);
} // end of GamePadController()
private Controller findGamePad(Controller[] cs)
/* Search the array of controllers until a suitable game pad
controller is found (eith of type GAMEPAD or STICK).
*/
{
Controller.Type type;
int i = 0;
while(i < cs.length) {
type = cs[i].getType();
if ((type == Controller.Type.GAMEPAD) ||
(type == Controller.Type.STICK))
break;
i++;
}
if (i == cs.length) {
System.out.println("No game pad found");
System.exit(0);
}
else
System.out.println("Game pad index: " + i);
return cs[i];
} // end of findGamePad()
private void findCompIndices(Controller controller)
/* Store the indices for the analog sticks axes
(x,y) and (z,rz), POV hat, and
button components of the controller.
*/
{
comps = controller.getComponents();
if (comps.length == 0) {
System.out.println("No Components found");
System.exit(0);
}
else
System.out.println("Num. Components: " + comps.length);
// get the indices for the axes of the analog sticks: (x,y) and (z,rz)
xAxisIdx = findCompIndex(comps, Component.Identifier.Axis.X, "x-axis");
yAxisIdx = findCompIndex(comps, Component.Identifier.Axis.Y, "y-axis");
zAxisIdx = findCompIndex(comps, Component.Identifier.Axis.Z, "z-axis");
rzAxisIdx = findCompIndex(comps, Component.Identifier.Axis.RZ, "rz-axis");
// get POV hat index
povIdx = findCompIndex(comps, Component.Identifier.Axis.POV, "POV hat");
findButtons(comps);
} // end of findCompIndices()
private int findCompIndex(Component[] comps,
Component.Identifier id, String nm)
/* Search through comps[] for id, returning the corresponding
array index, or -1 */
{
Component c;
for(int i=0; i < comps.length; i++) {
c = comps[i];
if ((c.getIdentifier() == id) && !c.isRelative()) {
System.out.println("Found " + c.getName() + "; index: " + i);
return i;
}
}
System.out.println("No " + nm + " component found");
return -1;
} // end of findCompIndex()
private void findButtons(Component[] comps)
/* Search through comps[] for NUM_BUTTONS buttons, storing
their indices in buttonsIdx[]. Ignore excessive buttons.
If there aren't enough buttons, then fill the empty spots in
buttonsIdx[] with -1's. */
{
buttonsIdx = new int[NUM_BUTTONS];
int numButtons = 0;
Component c;
for(int i=0; i < comps.length; i++) {
c = comps[i];
if (isButton(c)) { // deal with a button
if (numButtons == NUM_BUTTONS) // already enough buttons
System.out.println("Found an extra button; index: " + i + ". Ignoring it");
else {
buttonsIdx[numButtons] = i; // store button index
System.out.println("Found " + c.getName() + "; index: " + i);
numButtons++;
}
}
}
// fill empty spots in buttonsIdx[] with -1's
if (numButtons < NUM_BUTTONS) {
System.out.println("Too few buttons (" + numButtons +
"); expecting " + NUM_BUTTONS);
while (numButtons < NUM_BUTTONS) {
buttonsIdx[numButtons] = -1;
numButtons++;
}
}
} // end of findButtons()
private boolean isButton(Component c)
/* Return true if the component is a digital/absolute button, and
its identifier name ends with "Button" (i.e. the
identifier class is Component.Identifier.Button).
*/
{
if (!c.isAnalog() && !c.isRelative()) { // digital and absolute
String className = c.getIdentifier().getClass().getName();
// System.out.println(c.getName() + " identifier: " + className);
if (className.endsWith("Button"))
return true;
}
return false;
} // end of isButton()
private void findRumblers(Controller controller)
/* Find the rumblers. Use the last rumbler for making vibrations,
an arbitrary decision. */
{
// get the game pad's rumblers
rumblers = controller.getRumblers();
if (rumblers.length == 0) {
System.out.println("No Rumblers found");
rumblerIdx = -1;
}
else {
System.out.println("Rumblers found: " + rumblers.length);
rumblerIdx = rumblers.length-1; // use last rumbler
}
} // end of findRumblers()
// ----------------- polling and getting data ------------------
public void poll()
// update the component values in the controller
{
controller.poll();
}
public int getXYStickDir()
// return the (x,y) analog stick compass direction
{
if ((xAxisIdx == -1) || (yAxisIdx == -1)) {
System.out.println("(x,y) axis data unavailable");
return NONE;
}
else
return getCompassDir(xAxisIdx, yAxisIdx);
} // end of getXYStickDir()
public int getZRZStickDir()
// return the (z,rz) analog stick compass direction
{
if ((zAxisIdx == -1) || (rzAxisIdx == -1)) {
System.out.println("(z,rz) axis data unavailable");
return NONE;
}
else
return getCompassDir(zAxisIdx, rzAxisIdx);
} // end of getXYStickDir()
private int getCompassDir(int xA, int yA)
// Return the axes as a single compass value
{
float xCoord = comps[ xA ].getPollData();
float yCoord = comps[ yA ].getPollData();
// System.out.println("(x,y): (" + xCoord + "," + yCoord + ")");
int xc = Math.round(xCoord);
int yc = Math.round(yCoord);
// System.out.println("Rounded (x,y): (" + xc + "," + yc + ")");
if ((yc == -1) && (xc == -1)) // (y,x)
return NW;
else if ((yc == -1) && (xc == 0))
return NORTH;
else if ((yc == -1) && (xc == 1))
return NE;
else if ((yc == 0) && (xc == -1))
return WEST;
else if ((yc == 0) && (xc == 0))
return NONE;
else if ((yc == 0) && (xc == 1))
return EAST;
else if ((yc == 1) && (xc == -1))
return SW;
else if ((yc == 1) && (xc == 0))
return SOUTH;
else if ((yc == 1) && (xc == 1))
return SE;
else {
System.out.println("Unknown (x,y): (" + xc + "," + yc + ")");
return NONE;
}
} // end of getCompassDir()
public int getHatDir()
// Return the POV hat's direction as a compass direction
{
if (povIdx == -1) {
System.out.println("POV hat data unavailable");
return NONE;
}
else {
float povDir = comps[povIdx].getPollData();
if (povDir == POV.CENTER) // 0.0f
return NONE;
else if (povDir == POV.DOWN) // 0.75f
return SOUTH;
else if (povDir == POV.DOWN_LEFT) // 0.875f
return SW;
else if (povDir == POV.DOWN_RIGHT) // 0.625f
return SE;
else if (povDir == POV.LEFT) // 1.0f
return WEST;
else if (povDir == POV.RIGHT) // 0.5f
return EAST;
else if (povDir == POV.UP) // 0.25f
return NORTH;
else if (povDir == POV.UP_LEFT) // 0.125f
return NW;
else if (povDir == POV.UP_RIGHT) // 0.375f
return NE;
else { // assume center
System.out.println("POV hat value out of range: " + povDir);
return NONE;
}
}
} // end of getHatDir()
public boolean[] getButtons()
/* Return all the buttons in a single array. Each button value is
a boolean. */
{
boolean[] buttons = new boolean[NUM_BUTTONS];
float value;
for(int i=0; i < NUM_BUTTONS; i++) {
value = comps[ buttonsIdx[i] ].getPollData();
buttons[i] = ((value == 0.0f) ? false : true);
}
return buttons;
} // end of getButtons()
public boolean isButtonPressed(int pos)
/* Return the button value (a boolean) for button number 'pos'.
pos is in the range 1-NUM_BUTTONS to match the game pad
button labels.
*/
{
if ((pos < 1) || (pos > NUM_BUTTONS)) {
System.out.println("Button position out of range (1-" +
NUM_BUTTONS + "): " + pos);
return false;
}
if (buttonsIdx[pos-1] == -1) // no button found at that pos
return false;
float value = comps[ buttonsIdx[pos-1] ].getPollData();
// array range is 0-NUM_BUTTONS-1
return ((value == 0.0f) ? false : true);
} // end of isButtonPressed()
// ------------------- Trigger a rumbler -------------------
public void setRumbler(boolean switchOn)
// turn the rumbler on or off
{
if (rumblerIdx != -1) {
if (switchOn)
rumblers[rumblerIdx].rumble(0.8f); // almost full on for last rumbler
else // switch off
rumblers[rumblerIdx].rumble(0.0f);
rumblerOn = switchOn; // record rumbler's new status
}
} // end of setRumbler()
public boolean isRumblerOn()
{ return rumblerOn; }
} // end of GamePadController class
I think you are using the wrong design pattern here. You should use the observer pattern for this type of thing.
A polling loop not very efficient, and as you've noticed doesn't really yield the desired results.
I'm not sure what you are using inside your objects to detect if a key is pressed, but if it's a GUI architecture such as Swing or AWT it will be based on the observer pattern via the use of EventListeners, etc.
Here is a (slightly simplified) Observer-pattern
applied to your situation.
The advantage of this design is that when a button
is pressed and hold, method 'buttonChanged' will
still only be called once, instead of start
'repeating' every 50 ms.
public static final int BUTTON_01 = 0x00000001;
public static final int BUTTON_02 = 0x00000002;
public static final int BUTTON_03 = 0x00000004;
public static final int BUTTON_04 = 0x00000008; // hex 8 == dec 8
public static final int BUTTON_05 = 0x00000010; // hex 10 == dec 16
public static final int BUTTON_06 = 0x00000020; // hex 20 == dec 32
public static final int BUTTON_07 = 0x00000040; // hex 40 == dec 64
public static final int BUTTON_08 = 0x00000080; // etc.
public static final int BUTTON_09 = 0x00000100;
public static final int BUTTON_10 = 0x00000200;
public static final int BUTTON_11 = 0x00000400;
public static final int BUTTON_12 = 0x00000800;
private int previousButtons = 0;
void poll()
{
rockbandDrum.poll();
handleButtons();
}
private void handleButtons()
{
boolean[] buttons = getButtons();
int pressedButtons = getPressedButtons(buttons);
if (pressedButtons != previousButtons)
{
buttonChanged(pressedButtons); // Notify 'listener'.
previousButtons = pressedButtons;
}
}
public boolean[] getButtons()
{
// Return all the buttons in a single array. Each button-value is a boolean.
boolean[] buttons = new boolean[MAX_NUMBER_OF_BUTTONS];
float value;
for (int i = 0; i < MAX_NUMBER_OF_BUTTONS-1; i++)
{
int index = buttonsIndex[i];
if (index < 0) { continue; }
value = comps[index].getPollData();
buttons[i] = ((value == 0.0f) ? false : true);
}
return buttons;
}
private int getPressedButtons(boolean[] array)
{
// Mold all pressed buttons into a single number by OR-ing their values.
int pressedButtons = 0;
int i = 1;
for (boolean isBbuttonPressed : array)
{
if (isBbuttonPressed) { pressedButtons |= getOrValue(i); }
i++;
}
return pressedButtons;
}
private int getOrValue(int btnNumber) // Get a value to 'OR' with.
{
int btnValue = 0;
switch (btnNumber)
{
case 1 : btnValue = BUTTON_01; break;
case 2 : btnValue = BUTTON_02; break;
case 3 : btnValue = BUTTON_03; break;
case 4 : btnValue = BUTTON_04; break;
case 5 : btnValue = BUTTON_05; break;
case 6 : btnValue = BUTTON_06; break;
case 7 : btnValue = BUTTON_07; break;
case 8 : btnValue = BUTTON_08; break;
case 9 : btnValue = BUTTON_09; break;
case 10 : btnValue = BUTTON_10; break;
case 11 : btnValue = BUTTON_11; break;
case 12 : btnValue = BUTTON_12; break;
default : assert false : "Invalid button-number";
}
return btnValue;
}
public static boolean checkButton(int pressedButtons, int buttonToCheckFor)
{
return (pressedButtons & buttonToCheckFor) == buttonToCheckFor;
}
public void buttonChanged(int buttons)
{
if (checkButton(buttons, BUTTON_01)
{
drum.playSound("hiHat.wav");
}
if (checkButton(buttons, BUTTON_02)
{
drum.playSound("crash.wav");
}
}
Please post more information about the GamePadController class that you are using.
More than likely, that same library will offer an "event" API, where a "callback" that you register with a game pad object will be called as soon as the user presses a button. With this kind of setup, the "polling" loop is in the framework, not your application, and it can be much more efficient, because it uses signals from the hardware rather than a busy-wait polling loop.
Okay, I looked at the JInput API, and it is not really event-driven; you have to poll it as you are doing. Does the sound stop looping when you release the button? If so, is your goal to have the sound play just once, and not again until the button is release and pressed again? In that case, you'll need to track the previous button state each time through the loop.
Human response time is about 250 ms (for an old guy like me, anyway). If you are polling every 50 ms, I'd expect the controller to report the button depressed for several iterations of the loop. Can you try something like this:
boolean played = false;
while (true) {
String sound = null;
if (controller.isButtonPressed(1))
sound = "hiHat.wav";
if (controller.isButtonPressed(2))
sound = "crash.wav";
if (sound != null) {
if (!played) {
drum.playSound(sound);
played = true;
}
} else {
played = false;
}
Thread.sleep(50);
}

Categories