So I looked this error up before posting and understand it has something to do with trying to refer to an array value outside of the bounds of an array, but what confuses me is that my class still runs as if the error never happened (My guess for this is the differing threads), and that no one thing in my code seems to be causing the error (I followed the stack-trace all the way through); it's just annoying to see the error pop up every so often and I would like to know how to fix it.
My class is a text based grid display that allows for keyboard input (I don't extend the JFrame class because I don't want anyone to be able to resize the display other than by changing the "width" and "height" fields). I hooked it up to another class with a main method that uses the display to play a top-view text-based platformer game to make the error more visible. As I move around, every so often I get an error similar to this one:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
at javax.swing.text.CompositeView.getView(CompositeView.java:160)
at javax.swing.text.Utilities.getNextVisualPositionFrom(Utilities.java:1030)
at javax.swing.text.CompositeView.getNextEastWestVisualPositionFrom(CompositeView.java:757)
at javax.swing.text.CompositeView.getNextVisualPositionFrom(CompositeView.java:479)
at javax.swing.plaf.basic.BasicTextUI$RootView.getNextVisualPositionFrom(BasicTextUI.java:1588)
at javax.swing.plaf.basic.BasicTextUI.getNextVisualPositionFrom(BasicTextUI.java:1127)
at javax.swing.text.DefaultEditorKit$NextVisualPositionAction.actionPerformed(DefaultEditorKit.java:1690)
at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1663)
at javax.swing.JComponent.processKeyBinding(JComponent.java:2882)
at javax.swing.JComponent.processKeyBindings(JComponent.java:2929)
at javax.swing.JComponent.processKeyEvent(JComponent.java:2845)
at java.awt.Component.processEvent(Component.java:6310)
at java.awt.Container.processEvent(Container.java:2236)
at java.awt.Component.dispatchEventImpl(Component.java:4889)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1954)
at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:806)
at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1074)
at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:945)
at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:771)
at java.awt.Component.dispatchEventImpl(Component.java:4760)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Window.dispatchEventImpl(Window.java:2746)
at java.awt.Component.dispatchEvent(Component.java:4711)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:90)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
This is my class:
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class TextDisplay {
private int width;
private int height;
private int fontSize;
private double fontSpacing;
private char lastChar = 0;
private int lastKeyPress = 0;
private int lastKeyRelease = 0;
private JFrame mainframe;
private JTextPane field;
public TextDisplay(int width, int height) {
this.width = width;
this.height = height;
createGUI(false);
clearDisplay();
}
public TextDisplay(int width, int height, boolean visibility) {
this.width = width;
this.height = height;
createGUI(visibility);
clearDisplay();
}
private void createGUI(boolean visibility) {
mainframe = new JFrame();
if ((this.width < 1) || (this.height < 1)) {
throw new IllegalArgumentException();
}
mainframe.setResizable(false);
mainframe.setDefaultCloseOperation(3);
field = new JTextPane();
field.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
lastChar = e.getKeyChar();
}
public void keyPressed(KeyEvent e) {
lastKeyPress = e.getKeyCode();
}
public void keyReleased(KeyEvent e) {
lastKeyRelease = e.getKeyCode();
}
});
field.setEditable(false);
setFont(70, 0.0);
clearDisplay();
mainframe.add(field);
if (visibility) {
mainframe.setVisible(true);
}
}
public int getLastKeyPress() {
return lastKeyPress;
}
public int getLastKeyRelease() {
return lastKeyRelease;
}
public char getLastKeyTyped() {
return lastChar;
}
public void clearLastKeyPress() {
lastKeyPress = 0;
}
public void clearLastKeyRelease() {
lastKeyRelease = 0;
}
public void clearLastKeyTyped() {
lastChar = 0;
}
private int calcWindowWidth() {
FontMetrics fm = field.getFontMetrics(new Font("Consolas", 0, this.fontSize));
return 12 + fm.charWidth(' ') * this.width;
}
private int calcWindowHeight() {
FontMetrics fm = field.getFontMetrics(new Font("Consolas", 0, this.fontSize));
double s = this.fontSpacing;
int fh = fm.getHeight();
int h = this.height;
double tf = Math.signum(s) * -4;
int pixels = 40 + fh * h;
double spacingPixels = roundB(fh * s * (h - 1));
int oddPixels = (int) roundB(tf * Math.floor(8 * s / tf) - 8 * s);
int extraPixels = (int) spacingPixels;
return pixels + extraPixels - oddPixels;
}
public static double roundB(double value) {
return Math.round(value * 100000000000.0) / 100000000000.0;
}
public void refresh() {
mainframe.setVisible(false);
mainframe.setVisible(true);
}
public void dispose() {
mainframe.dispose();
}
public boolean isDisplayable() {
return mainframe.isDisplayable();
}
public void setVisible(boolean visibility) {
mainframe.setVisible(visibility);
}
public void setTitle(String name) {
mainframe.setTitle(name);
}
public int getFontSize() {
return this.fontSize;
}
public double getFontSpacing() {
return this.fontSpacing;
}
public void setFontSize(int fontSize) {
setFont(fontSize, this.fontSpacing);
}
public void setFontSpacing(double fontSpacing) {
setFont(this.fontSize, fontSpacing);
}
public void setFont(int fontSize, double fontSpacing) {
this.fontSize = fontSize;
this.fontSpacing = roundB(fontSpacing);
mainframe.setSize(calcWindowWidth(), calcWindowHeight());
StyledDocument doc = field.getStyledDocument();
MutableAttributeSet mas = new SimpleAttributeSet();
StyleConstants.setLineSpacing(mas, (float) this.fontSpacing);
StyleConstants.setFontSize(mas, this.fontSize);
StyleConstants.setFontFamily(mas, "Consolas");
doc.setParagraphAttributes(0, 1000, mas, true);
field.setStyledDocument(doc);
}
public void setDefaultCloseOperation(int operation) {
mainframe.setDefaultCloseOperation(operation);
}
public void clearDisplay() {
StringBuilder display = new StringBuilder();
for (int row = 0; row < this.height; row++) {
for (int col = 0; col < this.width; col++) {
display.append(' ');
}
display.append('\n');
}
field.setText(display.toString());
}
public void setDisplay(char[][] charMap) {
StringBuilder display = new StringBuilder();
if (charMap.length != this.height) {
throw new IllegalArgumentException("rows = " + charMap.length + ", this.height = " + this.height);
}
for (int row = 0; row < charMap.length; row++) {
if (charMap[row].length != this.width) {
throw new IllegalArgumentException(
"row = " + row + ", length = " + charMap[row].length + ", this.width = " + this.width);
}
char[] arrayOfChar;
int j = (arrayOfChar = charMap[row]).length;
for (int i = 0; i < j; i++) {
char c = arrayOfChar[i];
display.append(c);
}
display.append('\n');
}
field.setText(display.toString());
}
public void setDisplay(String[] lines) {
StringBuilder display = new StringBuilder();
if (lines.length != this.height) {
throw new IllegalArgumentException("rows = " + lines.length + ", this.height = " + this.height);
}
for (int i = 0; i < lines.length; i++) {
String string = lines[i];
if (string.length() != this.width) {
throw new IllegalArgumentException(
"row = " + i + ", length = " + string.length() + ", this.width = " + this.width);
}
display.append(string + '\n');
}
field.setText(display.toString());
}
public String[] getDisplay() {
return field.getText().split("\n");
}
public char[][] getDisplayCharMap() {
String[] display = getDisplay();
char[][] charMap = new char[this.height][this.width];
for (int row = 0; row < this.height; row++) {
charMap[row] = display[row].toCharArray();
}
return charMap;
}
public void setCharAt(char character, int row, int col) {
char[][] display = getDisplayCharMap();
if ((row >= this.height) || (col >= this.width) || (row < 0) || (col < 0)) {
throw new IllegalArgumentException("row = " + row + ", this.height = " + this.height + ", col = " + col
+ ", this.width = " + this.width);
}
display[row][col] = character;
setDisplay(display);
}
public char getCharAt(int row, int col) {
char[][] display = getDisplayCharMap();
if ((row >= this.height) || (col >= this.width) || (row < 0) || (col < 0)) {
throw new IllegalArgumentException("row = " + row + ", col = " + col);
}
return display[row][col];
}
public void output(String text, int row, int col) {
char[][] display = getDisplayCharMap();
if ((row >= this.height) || (col >= this.width) || (row < 0) || (col < 0)) {
throw new IllegalArgumentException("row = " + row + ", col = " + col);
}
char[] arrayOfChar = text.toCharArray();
for (char c : arrayOfChar) {
display[row][col] = c;
col++;
if (col >= this.width) {
col = 0;
row++;
}
if (row >= this.height) {
row = 0;
}
}
setDisplay(display);
}
public int getWidth() {
return this.width;
}
public int getHeight() {
return this.height;
}
}
This is the class I used with the main method:
import java.awt.event.KeyEvent;
public class Main {
volatile static int key;
public static void main(String[] args) {
TextDisplay display = new TextDisplay(10, 5);
display.setDisplay(new String[] {"OXXXXXXXXO","X X","X X","X X","OXXXXXXXXO"});
display.setVisible(true);
int x = 1;
int y = 1;
boolean gameRunning = true;
display.setCharAt('P', y, x);
while (gameRunning) {
key = display.getLastKeyPress();
if (key != 0) {
display.setCharAt(' ', y, x);
switch (key) {
case KeyEvent.VK_LEFT:
if (display.getCharAt(y, x - 1) == ' ') x--;
break;
case KeyEvent.VK_UP:
if (display.getCharAt(y - 1, x) == ' ') y--;
break;
case KeyEvent.VK_RIGHT:
if (display.getCharAt(y, x + 1) == ' ') x++;
break;
case KeyEvent.VK_DOWN:
if (display.getCharAt(y + 1, x) == ' ') y++;
break;
}
display.setCharAt('P', y, x);
display.clearLastKeyPress();
}
}
}
}
I can live with the error, but its quite annoying; could someone help me fix this?
EDIT: Figured this was impractical, so I just extended JFrame and wrapped any time I was changing the display with invokeLater. When I did this the error disappeared. Thanks to tsolakp for the help.
You are changing Swing UI state from main thread inside your while loop.
Keep in mind that after calling setVisible on display object the Event Dispatch Thread is started and you cannot modify UI directly after that call.
Swing does not guarantee proper operation in this type of usage.
Try to wrap all calls to display' variable aftersetVisiblecall withinvokeLater`.
1) Here is the code the uses invokeLater:
public static void main(String[] args) {
TextDisplay display = new TextDisplay(10, 5);
display.setDisplay(new String[] {"OXXXXXXXXO","X X","X X","X X","OXXXXXXXXO"});
display.setVisible(true);
Helper helper = new Helper(display);
boolean gameRunning = true;
while (gameRunning) {
SwingUtilities.invokeLater( () -> helper.check() );
}
}
private static class Helper{
int x = 1;
int y = 1;
TextDisplay display = null;
public Helper(TextDisplay display){
this.display = display;
display.setCharAt('P', y, x);
}
public void check(){
int key = display.getLastKeyPress();
if (key != 0) {
display.setCharAt(' ', y, x);
switch (key) {
case KeyEvent.VK_LEFT:
if (display.getCharAt(y, x - 1) == ' ') x--;
break;
case KeyEvent.VK_UP:
if (display.getCharAt(y - 1, x) == ' ') y--;
break;
case KeyEvent.VK_RIGHT:
if (display.getCharAt(y, x + 1) == ' ') x++;
break;
case KeyEvent.VK_DOWN:
if (display.getCharAt(y + 1, x) == ' ') y++;
break;
}
display.setCharAt('P', y, x);
display.clearLastKeyPress();
}
}
}
2) You dont really need to while loop at all. Just listen to key events. I just poster the approach with invokeLater so that you could familiarize how it can be used:
public static void main(String[] args) {
TextDisplay display = new TextDisplay(10, 5);
Helper helper = new Helper(display);
display.setDisplay(new String[] {"OXXXXXXXXO","X X","X X","X X","OXXXXXXXXO"});
display.addKeyListener(helper);
display.setVisible(true);
}
private static class Helper extends KeyAdapter{
int x = 1;
int y = 1;
TextDisplay display = null;
public Helper(TextDisplay display){
this.display = display;
display.setCharAt('P', y, x);
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key != 0) {
display.setCharAt(' ', y, x);
switch (key) {
case KeyEvent.VK_LEFT:
if (display.getCharAt(y, x - 1) == ' ') x--;
break;
case KeyEvent.VK_UP:
if (display.getCharAt(y - 1, x) == ' ') y--;
break;
case KeyEvent.VK_RIGHT:
if (display.getCharAt(y, x + 1) == ' ') x++;
break;
case KeyEvent.VK_DOWN:
if (display.getCharAt(y + 1, x) == ' ') y++;
break;
}
display.setCharAt('P', y, x);
}
}
}
and add this method to your TextDisplay:
public void addKeyListener(KeyListener kl){
field.addKeyListener(kl);
}
Related
I am trying to make a game which breaks the tile when I type a specific key.
Firstly, here is what the exception.
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
at sun.nio.cs.UTF_8.access$200(UTF_8.java:57)
at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:636)
at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)
at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.newLine(PrintStream.java:545)
at java.io.PrintStream.println(PrintStream.java:807)
at code.gui.NewView.addTileToDelete(NewView.java:192)
at code.gui.NewView.deleteTileByColor(NewView.java:151)
at code.gui.NewView.deleteTileByColor(NewView.java:158)
at code.gui.NewView.deleteTileByColor(NewView.java:152)
It runs fine if I execute deleteTileByColor method once,
however from the second time, above error occurs.
Here's the code for game.
package code.gui;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import code.handlers.PressKeyHandler;
import code.model.DataModel;
import code.parts.GamePanel;
import code.parts.GameTile;
public class NewView extends JFrame {
int _exitCounter;
int _tileDeleteCounter;
int _column = 4;
int _row = 4;
GamePanel _gamePanel;
DataModel _dm;
GameTile _tile;
ArrayList<ArrayList<GameTile>> _gameTileList = new ArrayList<ArrayList<GameTile>>();
ArrayList<GamePanel> _gamePanLst = new ArrayList<GamePanel>();
ArrayList<Character> _letterList;
public NewView(DataModel dm) {
super("Sequence Game");
this._dm = dm;
setLayout(new GridLayout(1, 4));
shuffleLetter();
for (int i = 0; i < _column; i++) {
_gamePanel = new GamePanel();
System.out.println("1. _gameTileList에 new ArrayList 총 " + i + "개 추가");
for (int j = 0; j < _row; j++) {
_tile = new GameTile(_letterList.get((4 * i) + j));
_gamePanel.add(_tile);
_tile.addKeyListener(new PressKeyHandler(_dm));
}
_gamePanLst.add(_gamePanel);
}
System.out.println("어레이 리스트<어레이리스트> 사이즈 : " + _gameTileList.size());
setFrame();
this.setSize(300, 500);
this.setVisible(true);
this.add(_gamePanel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_dm.setView(this);
}
public void setFrame() {
for (int i = 0; i < _gamePanLst.size(); i++) {
this.add(_gamePanLst.get(i), i);
}
this.repaint();
this.revalidate();
}
public void update(char letter) {
resetTileArrLst();
Color tileColor;
for (int i = 0; i < _column; i++) {
for (int j = 0; j < _row; j++) {
if (_gamePanLst.get(i).getComponent(j).getName().equals("" + letter)) {
System.out.println("i : " + i + " / j : " + j);
// System.out.println("최초비교");
tileColor = setTileColor(_gamePanLst.get(i).getComponent(j));
addTileToDelete(i, j);
deleteTileByColor(i, j, tileColor);
}
}
}
checkToQuit();
setFrame();
}
public void shuffleLetter() {
ArrayList<Character> temp = new ArrayList<Character>();
int numOfLetters = 26;
for (int i = 0; i < numOfLetters; i++) {
temp.add((char) (97 + (i)));
}
Collections.shuffle(temp);
_letterList = temp;
}
public Color setTileColor(Component comp) {
System.out.println("**********setTileColor********** ");
System.out.println("compo color : " + comp.getBackground());
Color temp;
if (comp.getBackground().equals(Color.RED)) {
temp = Color.RED;
} else if (comp.getBackground().equals(Color.BLUE)) {
temp = Color.BLUE;
} else if (comp.getBackground().equals(Color.YELLOW)) {
temp = Color.YELLOW;
} else {
temp = Color.GREEN;
}
return temp;
}
public GameTile getEmptyTile() {
GameTile emptyTile = new GameTile(' ');
emptyTile.setBorder(BorderFactory.createEmptyBorder());
emptyTile.setBackground(Color.BLACK);
emptyTile.addKeyListener(new PressKeyHandler(_dm));
return emptyTile;
}
public void checkToQuit() {
int counter = 0;
for (int i = 0; i < _gamePanel.getComponentCount(); i++) {
if (_gamePanel.getComponent(i).getBackground() == Color.BLACK) {
counter++;
}
}
if (counter == _gamePanel.getComponentCount()) {
this.dispose();
}
}
public void deleteTile(int i) {
_gamePanel.remove(i);
_gamePanel.add(getEmptyTile(), i);
}
public void deleteTileByColor(int column, int row, Color tileColor) {
// Right
if (((column + 1) < 4) && tileColor == (_gamePanLst.get((column + 1)).getComponent(row).getBackground())) {
column = column + 1;
addTileToDelete(column, row);
deleteTileByColor(column, row, tileColor);
}
// Left
if ((column - 1 >= 0) && tileColor == (_gamePanLst.get(column - 1).getComponent(row).getBackground())) {
column = column - 1;
addTileToDelete(column, row);
deleteTileByColor(column, row, tileColor);
}
// Below
if ((row + 1 < 4) && tileColor == (_gamePanLst.get(column).getComponent(row + 1).getBackground())) {
row = row + 1;
addTileToDelete(column, row);
deleteTileByColor(column, row, tileColor);
}
// Above
if ((row - 1 >= 0) && tileColor == (_gamePanLst.get(column).getComponent(row - 1).getBackground())) {
row = row - 1;
addTileToDelete(column, row);
deleteTileByColor(column, row, tileColor);
}
moveDownTiles();
}
public void moveDownTiles() {
for (int i = 0; i < _column; i++) {
for (int j = 0; j < _row; j++) {
if (_gamePanLst.get(i).getComponent(j).equals(_gameTileList.get(i).get(j))) {
_gamePanLst.get(i).remove(j);
_gamePanLst.get(i).add(getEmptyTile(), 0);
}
}
}
resetTileArrLst();
}
public void addTileToDelete(int column, int row) {
_gameTileList.get(column).set(row, (GameTile) _gamePanLst.get(column).getComponent(row));
System.out.println("지울 타일 추가 완료 : " + _gamePanLst.get(column).getComponent(row).getName());
}
public int get_exitCounter() {
return _exitCounter;
}
public void set_exitCounter(int _exitCounter) {
this._exitCounter = _exitCounter;
}
public GamePanel get_gamePanel() {
return _gamePanel;
}
public void set_gamePanel(GamePanel _gamePanel) {
this._gamePanel = _gamePanel;
}
public ArrayList<ArrayList<GameTile>> get_gameTilesList() {
return _gameTileList;
}
public void set_gameTilesList(ArrayList<ArrayList<GameTile>> _gameTilesList) {
this._gameTileList = _gameTilesList;
}
public ArrayList<Character> get_letterList() {
return _letterList;
}
public void set_letterList(ArrayList<Character> _letterList) {
this._letterList = _letterList;
}
public ArrayList<GamePanel> get_gamePanLst() {
return _gamePanLst;
}
public void set_gamePanLst(ArrayList<GamePanel> _gamePanLst) {
this._gamePanLst = _gamePanLst;
}
public void resetTileArrLst() {
if (_gameTileList.size() != 0) {
_gameTileList.clear();
}
for (int i = 0; i < _column; i++) {
_gameTileList.add(i, new ArrayList<GameTile>());
for (int j = 0; j < _row; j++) {
_gameTileList.get(i).add(getEmptyTile());
}
}
}
}
The problem asks for an acm graphics program that reads a txt file like this:
R
FUN
SALES
RECEIPT
MERE#FARM
DOVE###RAIL
MORE#####DRAW
HARD###TIED
LION#SAND
EVENING
EVADE
ARE
D
and makes a crossword puzzle, with blank squares on letters, black squares on '#', and nothing on empty spaces. The problem also asks that "if the square is at the beginning of a word running across, down, or both, the square should contain a number that is assigned sequentially through the puzzle."
I have the square drawing working, but I'm stuck on drawing the numbers correctly. There is something wrong with how I'm detecting null space and black squares. Can someone tell me what I'm doing wrong, please?
Here is the code:
import acm.program.*;
import java.io.*;
import java.util.*;
import acm.graphics.*;
import java.awt.*;
public class Crossword extends GraphicsProgram {
public void run() {
String fileName = "crosswordfile.txt";
makeCrosswordPuzzle(fileName);
}
private static final int sqCon = 15; // constant for square x and y dimensions
private int y = 0;
public void makeCrosswordPuzzle(String fileName) {
BufferedReader rd;
int y = 0; // y value for the square being added during that loop. increments by sqCon after every line
int wordNumber = 1; // variable for numbers added to certain boxes. increments every time the program adds a number
try {
rd = new BufferedReader(new FileReader(fileName));
String line = rd.readLine(); //reads one line of the text document at a time and makes it a string
while (line != null) {
int x = 0;
for (int i = 0; i < line.length(); i++) {
char lineChar = line.charAt(i);// the character being examined for each loop
GRect whiteSq = new GRect(sqCon,sqCon); //GRect for blank squares
GRect blackSq = new GRect(sqCon,sqCon);//GRect for black squares
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
if (lineChar == '#'){
add (blackSq,x,y);
}
if (Character.isLetter(lineChar)) {
add (whiteSq, x, y);
// if the element above or to the left of the current focus is null or blackSq, place the number and then increment wordNumber
GObject above = getElementAt(x+sqCon/2,y-sqCon/2);
GObject left = getElementAt(x-sqCon/2, y+sqCon/2);
GLabel wordNumberLabel = new GLabel(Integer.toString(wordNumber));
if (above == null || left == null || above == blackSq || left == blackSq) {
add(wordNumberLabel,x,y+sqCon);
wordNumber++;
}
}
x += sqCon;
}
line = rd.readLine();
y += sqCon;
}
rd.close();
}
catch (IOException e) {
throw new ErrorException(e);
}
}
}
Edited to add:
I copied your code over to my Eclipse and ran it. Here's the result.
You did fine on the upper half, but you missed the down numbers on the lower half.
Here's the same code, reformatted so it's easier to read.
import java.awt.Color;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import acm.graphics.GLabel;
import acm.graphics.GObject;
import acm.graphics.GRect;
import acm.program.GraphicsProgram;
import acm.util.ErrorException;
public class Crossword extends GraphicsProgram {
private static final long serialVersionUID = -7971434624427958742L;
public void run() {
// String fileName = "crosswordfile.txt";
String fileName = "C:/Eclipse/eclipse-4.2-work/com.ggl.testing/crosswordfile.txt";
makeCrosswordPuzzle(fileName);
}
private static final int sqCon = 15; // constant for square x and y
// dimensions
private int y = 0;
public void makeCrosswordPuzzle(String fileName) {
BufferedReader rd;
int y = 0; // y value for the square being added during that loop.
// increments by sqCon after every line
int wordNumber = 1; // variable for numbers added to certain boxes.
// increments every time the program adds a number
try {
rd = new BufferedReader(new FileReader(fileName));
String line = rd.readLine(); // reads one line of the text document
// at a time and makes it a string
while (line != null) {
int x = 0;
for (int i = 0; i < line.length(); i++) {
char lineChar = line.charAt(i);// the character being
// examined for each loop
GRect whiteSq = new GRect(sqCon, sqCon); // GRect for blank
// squares
GRect blackSq = new GRect(sqCon, sqCon);// GRect for black
// squares
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
if (lineChar == '#') {
add(blackSq, x, y);
}
if (Character.isLetter(lineChar)) {
add(whiteSq, x, y);
// if the element above or to the left of the current
// focus is null or blackSq, place the number and then
// increment wordNumber
GObject above = getElementAt(x + sqCon / 2, y - sqCon
/ 2);
GObject left = getElementAt(x - sqCon / 2, y + sqCon
/ 2);
GLabel wordNumberLabel = new GLabel(
Integer.toString(wordNumber));
if (above == null || left == null || above == blackSq
|| left == blackSq) {
add(wordNumberLabel, x, y + sqCon);
wordNumber++;
}
}
x += sqCon;
}
line = rd.readLine();
y += sqCon;
}
rd.close();
} catch (IOException e) {
throw new ErrorException(e);
}
}
}
I followed the advice of my own comment. I created the crossword puzzle answer, numbered the crossword puzzle answer, and finally drew the crossword puzzle answer.
Here's the applet result:
I kept a List of crossword puzzle cells. That way, I could determine the length and the width of the puzzle by the number of characters on a row and the number of rows of the input text file. I didn't have to hard code the dimensions.
For each crossword cell, I kept track of whether or not it was a letter, and whether or not it was a dark space.
When determining where to put the numbers, I followed 2 rules.
An across number is placed where the cell left of the cell is empty or dark, and there are three or more letters across.
A down number is placed where the cell above the cell is empty or dark, there are three or more letters down, and there is no across number.
You can see in the code that I had to do some debug printing to get the crossword puzzle clue numbering correct. I broke the process into many methods to keep each method as simple as possible.
Finally, I drew the crossword puzzle answer from the information in the List.
Here's the code:
import java.awt.Color;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import acm.graphics.GLabel;
import acm.graphics.GRect;
import acm.program.GraphicsProgram;
import acm.util.ErrorException;
public class Crossword extends GraphicsProgram {
private static final boolean DEBUG = false;
private static final long serialVersionUID = -7971434624427958742L;
private List<CrosswordCell> crosswordCellList;
#Override
public void run() {
this.crosswordCellList = new ArrayList<CrosswordCell>();
// String fileName = "crosswordfile.txt";
String fileName = "C:/Eclipse/eclipse-4.2-work/" +
"com.ggl.testing/crosswordfile.txt";
try {
readCrosswordAnswer(fileName);
if (DEBUG) printCrosswordAnswer();
numberCrosswordCells();
if (DEBUG) printCrosswordAnswer();
drawCrosswordAnswer();
} catch (FileNotFoundException e) {
throw new ErrorException(e);
} catch (IOException e) {
throw new ErrorException(e);
}
}
private void readCrosswordAnswer(String fileName)
throws FileNotFoundException, IOException {
BufferedReader reader =
new BufferedReader(new FileReader(fileName));
String line = "";
int row = 0;
while ((line = reader.readLine()) != null) {
for (int column = 0; column < line.length(); column++) {
CrosswordCell cell = new CrosswordCell(column, row);
char lineChar = line.charAt(column);
if (lineChar == '#') {
cell.setDarkCell(true);
} else if (Character.isLetter(lineChar)) {
cell.setLetter(true);
}
crosswordCellList.add(cell);
}
row++;
}
reader.close();
}
public void printCrosswordAnswer() {
for (CrosswordCell cell : crosswordCellList) {
System.out.println(cell);
}
}
private void numberCrosswordCells() {
int clueNumber = 1;
for (CrosswordCell cell : crosswordCellList) {
if (cell.isLetter()) {
clueNumber = testCell(cell, clueNumber);
}
}
}
private int testCell(CrosswordCell cell, int clueNumber) {
Point p = cell.getLocation();
CrosswordCell leftCell = getLeftCell(p.x, p.y);
List<CrosswordCell> acrossList = getRightCells(p.x, p.y);
if (DEBUG) {
System.out.print(p);
System.out.println(", " + leftCell + " " +
acrossList.size());
}
if ((leftCell == null) && (acrossList.size() >= 3)) {
cell.setClueNumber(clueNumber++);
} else {
CrosswordCell aboveCell = getAboveCell(p.x, p.y);
List<CrosswordCell> downList = getBelowCells(p.x, p.y);
if (DEBUG) {
System.out.print(p);
System.out.println(", " + aboveCell + " " +
downList.size());
}
if ((aboveCell == null) && (downList.size() >= 3)) {
cell.setClueNumber(clueNumber++);
}
}
return clueNumber;
}
private CrosswordCell getAboveCell(int x, int y) {
int yy = y - 1;
return getCell(x, yy);
}
private CrosswordCell getLeftCell(int x, int y) {
int xx = x - 1;
return getCell(xx, y);
}
private List<CrosswordCell> getBelowCells(int x, int y) {
List<CrosswordCell> list = new ArrayList<CrosswordCell>();
for (int i = y; i < (y + 3); i++) {
CrosswordCell cell = getCell(x, i);
if (cell != null) {
list.add(cell);
}
}
return list;
}
private List<CrosswordCell> getRightCells(int x, int y) {
List<CrosswordCell> list = new ArrayList<CrosswordCell>();
for (int i = x; i < (x + 3); i++) {
CrosswordCell cell = getCell(i, y);
if (cell != null) {
list.add(cell);
}
}
return list;
}
private CrosswordCell getCell(int x, int y) {
for (CrosswordCell cell : crosswordCellList) {
Point p = cell.getLocation();
if ((p.x == x) && (p.y == y)) {
if (cell.isDarkCell()) {
return null;
} else if (cell.isLetter()){
return cell;
} else {
return null;
}
}
}
return null;
}
private void drawCrosswordAnswer() {
int sqCon = 32;
for (CrosswordCell cell : crosswordCellList) {
Point p = cell.getLocation();
if (cell.isDarkCell()) {
drawDarkCell(p, sqCon);
} else if (cell.isLetter()) {
drawLetterCell(cell, p, sqCon);
}
}
}
private void drawDarkCell(Point p, int sqCon) {
GRect blackSq = new GRect(sqCon, sqCon);
blackSq.setFilled(true);
blackSq.setFillColor(Color.BLACK);
add(blackSq, p.x * sqCon, p.y * sqCon);
}
private void drawLetterCell(CrosswordCell cell, Point p, int sqCon) {
GRect whiteSq = new GRect(sqCon, sqCon);
add(whiteSq, p.x * sqCon, p.y * sqCon);
if (cell.getClueNumber() > 0) {
String label = Integer.toString(cell.getClueNumber());
GLabel wordNumberLabel = new GLabel(label);
add(wordNumberLabel, p.x * sqCon + 2, p.y * sqCon + 14);
}
}
class CrosswordCell {
private boolean darkCell;
private boolean isLetter;
private int clueNumber;
private Point location;
public CrosswordCell(int x, int y) {
this.location = new Point(x, y);
this.clueNumber = 0;
this.darkCell = false;
this.isLetter = false;
}
public boolean isDarkCell() {
return darkCell;
}
public void setDarkCell(boolean darkCell) {
this.darkCell = darkCell;
}
public boolean isLetter() {
return isLetter;
}
public void setLetter(boolean isLetter) {
this.isLetter = isLetter;
}
public int getClueNumber() {
return clueNumber;
}
public void setClueNumber(int clueNumber) {
this.clueNumber = clueNumber;
}
public Point getLocation() {
return location;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("CrosswordCell [location=");
builder.append(location);
builder.append(", clueNumber=");
builder.append(clueNumber);
builder.append(", darkCell=");
builder.append(darkCell);
builder.append(", isLetter=");
builder.append(isLetter);
builder.append("]");
return builder.toString();
}
}
}
I have three classes, Organism, Creature and Plant. Creature and Plant both extend Organism. Now, this code worked when everything was bundled into Organism, so I assume the GUI class isn't at fault. Basically, what is happening is only Creature specific stats are being generated, whereas the Organism stats are being ignored, returning 0. I cannot find anything wrong with it, so I thought you guys could help. (Ignore the mess, I am still playing around.)
Creature Class
import java.awt.Color;
import java.util.Random;
public class Creature extends Organism
{
private char gender;
private int strength, speed, diet, aggression;
private int mated, maxMated;
public Creature()
{
super();
Random rand = new Random();
if(rand.nextInt(2) == 0)
{
gender = 'f';
}
else
{
gender = 'm';
}
mated = 0;
setStats();
}
public Creature(String gen[])
{
super(gen);
Random rand = new Random();
if(rand.nextInt(2) == 0)
{
gender = 'f';
}
else
{
gender = 'm';
}
x = rand.nextInt(maxX);
y = rand.nextInt(maxY);
setStats();
}
public Creature(String gen[], int newX, int newY)
{
super(gen, newX, newY);
this.gene = gen;
Random rand = new Random();
if(rand.nextInt(2) == 0)
{
gender = 'f';
}
else
{
gender = 'm';
}
isAlive = true;
x = newX;
y = newY;
setStats();
}
public int getAggro()
{
return aggression;
}
public int getDiet()
{
return diet;
}
public int getSpeed()
{
return speed;
}
public void setStats()
{
strength = (gene[1].charAt(0)-48) + 1 + ((gene[1].charAt(1)-48) * (gene[1].charAt(2)-48));
speed = (strength + (gene[2].charAt(0)-48) - (gene[2].charAt(1)-48) + (gene[2].charAt(2)-48))/(size + 1);
diet = (gene[7].charAt(0)-48) + 1 + ((gene[7].charAt(1)-48) * (gene[7].charAt(2)-48)*(3/4));
aggression = ((strength + size)/2) / (gene[8].charAt(0)-48 + gene[8].charAt(1)-48 + gene[8].charAt(2)-48 + 1);
maxHealth = 64 + size + (strength / 2);
maxHunger = 100 + (size - speed - aggression);
health = maxHealth;
hunger = maxHunger;
}
public Creature breed(Creature mate)
{
Random rand = new Random();
int x = rand.nextInt(gene.length);
int y = rand.nextInt(gene[x].length());
String geneY[] = new String[16];
int i;
for(i = 0; i < x; i++)
{
geneY[i] = gene[i];
}
geneY[x] = gene[x].substring(0,y);
geneY[x] = geneY[x] + mate.gene[x].substring(y);
for(i = x + 1; i < 16; i++)
{
geneY[i] = mate.gene[i];
}
char newGender;
if(rand.nextInt(2) == 0)
{
newGender = 'f';
}
else
{
newGender = 'm';
}
hunger = hunger /2;
mate.hunger = mate.hunger /2;
mated = mated + 1;
mate.mated = mate.mated + 1;
Creature temp = new Creature(geneY,this.getX(),this.getY());
temp.mutate();
return temp;
}
public void eat(Organism b) //A eats B
{
b.isAlive = false;
this.hunger = this.hunger + b.size;
}
public boolean isCompatible(Creature org)
{
int differences = 0;
for(int i = 0; i < this.gene.length; i++)
{
if(!this.gene[i].equals(org.gene[i]))
{
differences = differences + 1;
}
}
if(differences > 1 || this.gender == org.gender || mated == maxMated || org.mated == org.maxMated)
{
return false;
}
return true;
}
public void moveTo(Organism org)
{
int vectX, vectY, moveX = 0, moveY = 0;
double angle;
vectX = this.x - org.x;
vectY = this.y - org.y;
if(vectX == 0)
{
moveY = this.speed;
}
if(vectY == 0)
{
moveX = this.speed;
}
if(vectX == 0 && vectY == 0)
{
moveX = 0;
moveY = 0;
}
if(vectX != 0 && vectY != 0)
{
angle = ((Math.atan((vectY)/(vectX)))/(2*Math.PI))*360;
if(angle < 0)
{
angle = angle * - 1;
}
moveX = (int)(Math.sin(angle)*this.speed);
moveY = (int)(Math.cos(angle)*this.speed);
}
if(Math.sqrt((vectX*vectX)+(vectY*vectY)) < speed)
{
if(vectX > 0)
{
this.x = this.x - vectX;
}
else
{
this.x = this.x + vectX;
}
if(vectY > 0)
{
this.y = this.y - vectY;
}
else
{
this.y = this.y + vectY;
}
}
else
{
if(vectX > 0)
{
this.x = this.x - moveX;
}
else
{
this.x = this.x + moveX;
}
if(vectY > 0)
{
this.y = this.y - moveY;
}
else
{
this.y = this.y + moveY;
}
}
}
}
Organism Class
import java.awt.Color;
import java.util.Random;
public class Organism
{
protected String gene[] = new String[16];
protected boolean isAlive;
protected int x;
protected int y;
protected int size, sense, fertility, scent;
protected int health, hunger, maxHealth, maxHunger;
protected int maxX = 1000;
protected int maxY = 1000;
private Color color = new Color(255,0,0);
public Organism()
{
Random rand = new Random();
for(int i = 0; i < 16; i++)
{
gene[i] = ""+ rand.nextInt(4) + rand.nextInt(4) + rand.nextInt(4);
}
isAlive = true;
x = rand.nextInt(maxX);
y = rand.nextInt(maxY);
setStats();
}
public Organism(String gen[])
{
Random rand = new Random();
this.gene = gen;
isAlive = true;
x = rand.nextInt(maxX);
y = rand.nextInt(maxY);
setStats();
}
public Organism(String gen[], int newX, int newY)
{
this.gene = gen;
isAlive = true;
x = newX;
y = newY;
setStats();
}
public Color getColor()
{
return color;
}
public int getX()
{
return x;
}
public void setX(int tempX)
{
this.x = tempX;
}
public int getY()
{
return y;
}
public void setY(int tempY)
{
this.y = tempY;
}
public int getHunger()
{
return hunger;
}
public void setHunger(int hun)
{
this.hunger = hun;
}
public int getHealth()
{
return health;
}
public void setHealth(int heal)
{
this.health = heal;
}
public void setStats()
{
size = (gene[0].charAt(0)-48) + 1 + ((gene[0].charAt(1)-48) * (gene[0].charAt(2)-48));
sense = (gene[5].charAt(2)-48) + 1 + ((gene[5].charAt(1)-48) * (gene[5].charAt(2)-48));
fertility = 22 - size + (gene[6].charAt(0)-48) + 1 + ((gene[6].charAt(1)-48) * (gene[6].charAt(2)-48));
scent = (gene[8].charAt(0)-48 + gene[8].charAt(1)-48 + gene[8].charAt(2)-48);
}
public int getSize()
{
return size;
}
public int getSense()
{
return sense;
}
public boolean getAlive()
{
return isAlive;
}
public void setAlive(boolean live)
{
this.isAlive = live;
}
public String getInfo()
{
String info;
info = "Health: " + this.health + ", Hunger: "+ this.hunger + ", Status: " + this.isAlive;
return info;
}
/*public String getStats()
{
String info = "Size: " + this.size + ", Strength: " + this.strength +
", Speed: " + this.speed + ", Sight: " + this.sight +
", Smell: " + this.smell + ", Hearing: " + this.hearing +
", Fertility: " + this.fertility + ", Diet: " + this.diet +
", Aggression: " + this.aggression + ", Scent: " + this.scent;
return info;
}*/
public String displayGene()
{
String geneP = "|";
for(int i = 0; i < gene.length; i++)
{
for(int j = 0; j < gene[i].length(); j++)
{
switch (gene[i].charAt(j))
{
case '0': geneP = geneP + 'A';
break;
case '1': geneP = geneP + 'T';
break;
case '2': geneP = geneP + 'C';
break;
case '3': geneP = geneP + 'G';
break;
}
}
geneP = geneP + "|";
}
return geneP;
}
public void mutate()
{
Random rand = new Random();
int i = rand.nextInt(10000) + 1;
int affected;
if(i > 9999)
{
affected = rand.nextInt(gene.length);
i = rand.nextInt(gene[affected].length());
int j = rand.nextInt(4);
gene[affected] = gene[affected].substring(0,i)+j+gene[affected].substring(i+1);
}
}
public void hungerCheck()
{
hunger = hunger - (size / 10 + 1);
if(hunger <= 0)
{
health = health - 1;
}
if(hunger > (maxHunger * 3)/4)
{
health = health + 1;
}
}
public void validate()
{
if(x > maxX)
{
x = 100;
}
if(x < 0)
{
x = 0;
}
if(y > maxY)
{
y = 100;
}
if(y < 0)
{
y = 0;
}
if(hunger > maxHunger)
{
hunger = maxHunger;
}
if(hunger <= 0)
{
hunger = 0;
}
if(health <= 0)
{
isAlive = false;
}
if(health > maxHealth)
{
health = maxHealth;
}
}
}
You have to call the super method from the Creature.setStats() method
public class Creature extends Organism
public void setStats()
{
super.setStats(); // Call to Organism.setStats()
...
}
....
}
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);
}
I know that there are lots of examples out there on this, but they all feel a little dated (even the sun docs) so I'm asking here to make sure what I'm doing is up to date. How would I go about talking to javascript from inside a java applet? Something simple like how to call alert() is all I'm looking for. Bonus points for a way to check if the browser has javascript enabled (this applet may be used in a school setting where having javascript turned off is a real possibility). All help is greatly appreciated, thanks in advance...
Code:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import netscape.javascript.JSObject;
public class TeamProject extends Applet implements ActionListener, MouseListener {
char[][] charValues = new char[10][10];
Table aTable;
boolean allowUserInput = false;
Button BtnStart;
Button randomChangeBtn;
boolean guessMode;
JSObject jso;
public void init() {
//setup buttons
BtnStart = new Button("add row/column");
BtnStart.addActionListener((ActionListener)this); //cast
randomChangeBtn = new Button("change one value");
randomChangeBtn.addActionListener((ActionListener)this);
//add button
this.add(BtnStart);
//add image to Image objects
Image imgO = getImage(getCodeBase(), "images/not.gif");
Image imgX= getImage(getCodeBase(), "images/cross.gif");
//setup table
aTable = new Table(100, 100, 75, 55, 5, 5, imgX, imgO);
//setBackground(Color.LIGHT_GRAY);
super.resize(700, 700);
//add mouse listener
addMouseListener(this);
//initially guessMode will be false
guessMode = false;
//to talk to javascript
jso = JSObject.getWindow(this);
}
public void paint(Graphics g) {
g.setColor(Color.black);
aTable.draw(g);
}
//Mouse listener methods
public void mousePressed (MouseEvent e) {
if(!guessMode){
if ((allowUserInput)) { //&&(aTable.isDrawable(e.getX(), e.getY())))
aTable.swapSquareValue(e.getX(), e.getY());
repaint();
}
} else {
System.out.println("guessed row = " + e.getY() + " guessed col = " + e.getX());
aTable.checkGuess(e.getX(), e.getY());
//repaint();
}
}
public void mouseClicked (MouseEvent e) {}
public void mouseEntered (MouseEvent e) {}
public void mouseReleased (MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
//Button action listener
public void actionPerformed(ActionEvent e) {
if (e.getSource() == BtnStart) {
aTable.addRow();
aTable.addColumn();
this.remove(BtnStart);
this.add(randomChangeBtn);
repaint();
} else if (e.getSource() == randomChangeBtn) {
//aTable.addRow();
aTable.randomChangeFunc();
repaint();
guessMode = true;
}
allowUserInput = true;
System.out.println(aTable.toString());
}
}
and my Table class:
import java.awt.*;
import java.util.Random;
public class Table {
private char[][]values = new char[10][10]; //probably better to use array of integer values(0 or 1)
Image imgO;
Image imgX;
private int Rows;
private int Columns;
private int BoxWidth ;
private int BoxHeight;
public Point Pos = new Point();
private int tableHeight;
private int tableWidth;
private int changedRow;
private int changedCol;
//constructor
public Table(int x, int y, int width, int height, int col, int rows, Image X, Image O) {
Rows = rows;
Columns = col;
BoxWidth = width;
BoxHeight = height;
Pos.x = x;
Pos.y = y;
imgX = X;
imgO = O;
tableHeight = Rows*BoxHeight;
tableWidth = Columns*BoxWidth;
this.setValues();
}
//draw table
public void draw(Graphics g) {
//draw vertical table lines
for (int i = 0 ; i <= Columns ; i++)
g.drawLine(i*BoxWidth + Pos.x, Pos.y, i*BoxWidth + Pos.x, tableHeight+Pos.y);
//draw horizontal table line
for(int i = 0 ; i <= Rows ; i++)
g.drawLine(Pos.x, i*BoxHeight + Pos.y, tableWidth+Pos.x, i*BoxHeight + Pos.y);
//draw values
drawValues(g);
}
public void swapSquareValue(int x, int y) {
if (this.isDrawable(x, y)) {
int col = this.getColumn(x)-1;
int row = this.getRow(y)-1;
if(values[row][col] == 'X')
values[row][col] = 'O';
else if(values[row][col] == 'O')
values[row][col] = 'X';
else
System.out.println("ERROR SWAPPING SQUARE VALUE");
} else
System.out.println("says not drawable");
}
public char getValue(int col, int row) {
return values[row][col];
}
//return true if (x,y) is a point in the table
public boolean isDrawable(int x, int y) {
if((this.getRow(y)!=-1)||(this.getColumn(x)!=-1))
return true;
else
return false;
}
public void addRow() {
Rows++;
tableHeight = (Rows*BoxHeight);
int numOfXs = 0;
for (int c=0; c < Columns; c++) {
numOfXs = 0;
for(int r = 0; r < Rows - 1; r++) {
if(values[r][c] == 'X'){
numOfXs++;
System.out.println("in column " + c + "new x found at " + r + " " + c + ", numOfXs = " + numOfXs);
}
if(numOfXs % 2 == 0) {
values[Rows - 1][c] = 'O';
} else{
values[Rows - 1][c] = 'X';
}
}//end inner for
System.out.println("end of column " + c);
}//end outer for
}// end function
public void addColumn() {
Columns++;
tableWidth = (Columns*BoxWidth);
int numOfXs = 0;
for (int r=0; r < Rows; r++) {
numOfXs = 0;
for(int c = 0; c < Columns - 1; c++) {
if(values[r][c] == 'X') {
numOfXs++;
System.out.println("in row " + r + "new x found at " + r + " " + c + ", numOfXs = " + numOfXs);
}
if(numOfXs % 2 == 0) {
values[r][Columns - 1] = 'O';
}
else {
values[r][Columns - 1] = 'X';
}
}//end inner for
System.out.println("end of row " + r);
}
}
//does not add or remove values
public void setColumn(int col) {
Columns = col;
tableWidth = (Columns*BoxWidth);
}
//does not add or remove values
public void setRows(int row) {
Rows = row;
tableHeight = (row*BoxHeight);
}
public String toString() {
String ValueString = "Displaying charValues[" + Rows + "][" + Columns + "]\n";
for (int r=0; r < Rows; r++) {
for (int c=0; c < Columns; c++) {
ValueString += (char)values[r][c];
}
ValueString += "\n"; //next line
}
return ValueString;
}
private void drawValues(Graphics g) {
Point drawPoint = new Point();
for (int r=0; r < Rows; r++)
for (int c=0; c < Columns; c++) {
drawPoint.x = Pos.x+BoxWidth*c;
drawPoint.y = Pos.y+BoxHeight*r;
//g.setColor(Color.white);
//g.fillRect(drawPoint.x+1, drawPoint.y+1, BoxWidth-1, BoxHeight-1);
if (values[r][c] == 'X') {
g.drawImage(imgX,drawPoint.x+1, drawPoint.y+1, BoxWidth-1, BoxHeight-1, null);
} else {
g.drawImage(imgO,drawPoint.x+1, drawPoint.y+1, BoxWidth-1, BoxHeight-1, null);
}
//System.out.print((char)values[r][c]);
}
g.setColor(Color.black);
}
//fills array with random values
private void setValues() {
for (int r=0; r < Rows; r++)
for (int c=0; c < Columns; c++) {
values[r][c] = this.randomChar();
}
}
//randomly return 'X' or 'O'
private char randomChar() {
char randomValue;
Random RandomGen = new Random();
if (RandomGen.nextInt(2)==0)
randomValue = 'O';
else
randomValue ='X';
return randomValue;
}
private int getColumn(int x) {
int offsetx=0;
for (int i = 0 ; i < Columns ; i++) {
offsetx = i*BoxWidth;
if((x>Pos.x+offsetx)&& (x<Pos.x+offsetx+BoxWidth))
return i+1;
}
return -1;
}
private int getRow(int y) {
int offsety=0;
for (int i = 0 ; i < Rows ; i++) {
offsety = i*BoxHeight;
if((y>Pos.y+offsety)&& (y<Pos.x+offsety+BoxHeight))
return i+1;
}
return -1;
}
public void randomChangeFunc() {
//get random row and column
Random rand=new Random();
int randRow = rand.nextInt(Rows);
int randCol = rand.nextInt(Columns);
System.out.println("randRow = " + randRow + " randCol = " + randCol);
/*THIS SHOULD BE HANDLED BY swapSquareValue(randCol,randRow)
/*BUT GETTING ERRORS (notDrawable). THE FOLLOWING CODE IS A WORK-AROUND
*/
if(values[randRow][randCol] == 'X')
values[randRow][randCol] = 'O';
else if(values[randRow][randCol] == 'O')
values[randRow][randCol] = 'X';
else
System.out.println("ERROR SWAPPING SQUARE VALUE");
//set globals
changedRow = randRow;
changedCol = randCol;
}
public void checkGuess(int guessCol, int guessRow){
int gCol = this.getColumn(guessCol)-1;
int gRow = this.getRow(guessRow)-1;
System.out.println("gCol = " + gCol + " gRow = " + gRow);
if(gCol == changedCol && gRow == changedRow) {
System.out.println("CORRECT!!!");
} else
System.out.println("incorrect :(");
}
}
Changing my javac command to:
javac -classpath /usr/lib/Java6u1/jre/lib/plugin.jar TeamProject.java
ignores my "Table" class and I get errors where I mention that. Any ideas?
Look at this article. If you try calling JS from applet on this page it definitely works, because there are js exception after update action from applet :)
import netscape.javascript.JSObject
public void init()
{
jso = JSObject.getWindow(this);
}
public void actionPerformed(ActionEvent e) {
if(jso != null )
try {
jso.call("updateWebPage", new String[] {txt.getText()});
}
catch (Exception ex) {
ex.printStackTrace();
}
}
EDIT:
For the classpath problem you need to add plugin.jar to your classpath which is located in %JAVA_HOME%\jre\lib\plugin.jar
EDIT2:
I think that your problem is that a class Table isn't compiled too:
try javac -classpath /usr/lib/Java6u1/jre/lib/plugin.jar TeamProject.java Table.java or use wildcards like *.java.
Maybe you should consider use IDE like Eclipse or Netbeans to compile and run project instead of struggling with command line tools.
As requested a sample which calls alert on the page it is embedded in (tested in Opera 10.01, FF 3.5.4, IE6).
Note the MAYSCRIPT in the applet tag, this MUST be present to enable java-javascript communication. As per default access to JSObject is disabled due to security reasons.
HTML:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<applet code="HelloWWW.class" width="300px" height="100px" MAYSCRIPT></applet>
</body>
</html>
Java (compile with javac -cp .;[pathtojre]/jre/lib/plugin.jar HelloWWW.java)
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import netscape.javascript.*;
public class HelloWWW extends Applet implements ActionListener {
Button runButton;
public void init() {
runButton = new Button("Run: alert(\"Hello WWW\")");
add(runButton);
runButton.addActionListener(this);
}
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == runButton) {
try {
//get JSOBject
JSObject jso = JSObject.getWindow(this);
//call alert with parameter passed as Object array
jso.call("alert", new Object[]{"Hello WWW"});
} catch (JSException e) {
e.printStackTrace();
}
runButton.setLabel("Did it!");
repaint();
}
}
}
Also check Java-to-Javascript Communication and Mozilla Dev: JSObject for further info.