I am working on an Virtual-Reality client for a Java API
I am not able to read the second command (1 byte ord) from the Java Server-Socket.
This is my java code:
package de.e_nexus.vr.server;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import de.e_nexus.vr.server.codes.Client2ServerCode;
import de.e_nexus.vr.server.listeners.VRClientRequestAppInfo;
import de.e_nexus.vr.server.listeners.VRClientStatusListener;
import de.e_nexus.vr.server.listeners.VRExceptionListener;
import de.e_nexus.vr.server.mesh.Mesh;
import de.e_nexus.vr.server.mesh.MeshOutputStream;
public class VRServer extends ServerSocket {
private static final Charset LATIN1;
static {
LATIN1 = Charset.forName("latin1");
}
private final Set<VRClientStatusListener> statusListeners = new LinkedHashSet<VRClientStatusListener>();
private final Set<VRExceptionListener> exceptionListeners = new LinkedHashSet<VRExceptionListener>();
private final Set<VRClientRequestAppInfo> infoListeners = new LinkedHashSet<VRClientRequestAppInfo>();
private final Set<Mesh> toSend = new LinkedHashSet<Mesh>();
private class Worker extends Thread {
private boolean running = true;
public void run() {
try {
setSoTimeout(50);
} catch (SocketException e) {
e.printStackTrace();
}
while (running) {
cycle();
}
T = null;
};
}
private Worker T;
public VRServer() throws IOException {
super(8779);
}
public void addVRClientStatusListener(VRClientStatusListener vrClientStatusListener) {
statusListeners.add(vrClientStatusListener);
}
public void addVRExceptionListener(VRExceptionListener listener) {
exceptionListeners.add(listener);
}
public void start() {
if (T == null) {
T = new Worker();
T.start();
}
}
public void stop() {
T.running = false;
}
public boolean isStopping() {
return T != null && !T.running;
}
protected void cycle() {
try {
Socket s = accept();
log("Connected");
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
int read = in.read();
if (read == -1) {
log("Closed unexpectedly "+in.available());
} else {
Client2ServerCode code = Client2ServerCode.read(read);
log("Read " + code + " ord(" + read + ")");
switch (code) {
case GET_APP_INFO:
StringBuilder sb = new StringBuilder();
for (VRClientRequestAppInfo vrClientRequestAppInfo : infoListeners) {
sb.append(vrClientRequestAppInfo.getLatin1Title());
}
System.out.println(sb);
outLenString(out, sb.toString());
break;
case GET_INCOMING_MESH:
int count = Math.min(toSend.size(), 100);
out.write(count);
System.out.println("wrote incomming mesah count");
for (int i = 0; i < count; i++) {
Iterator<Mesh> iterator = toSend.iterator();
Mesh mesh = iterator.next();
iterator.remove();
ByteArrayOutputStream buff = new ByteArrayOutputStream();
MeshOutputStream mos = new MeshOutputStream(buff);
mos.writeMesh(mesh);
mos.flush();
outLenString(out, buff.size() + "");
out.write(buff.toByteArray());
}
out.flush();
default:
break;
}
System.out.println("Thanks");
}
s.close();
} catch (Exception e) {
notifyExceptionInCycle(e);
}
}
private void log(String string) {
System.out.println(string);
}
private void outLenString(OutputStream out, String string) throws IOException {
byte[] b = string.getBytes(LATIN1);
int length = b.length;
out.write(length);
out.write(b);
out.flush();
}
private void notifyExceptionInCycle(Exception e) {
boolean handled = false;
for (VRExceptionListener vrExceptionListener : exceptionListeners) {
try {
vrExceptionListener.handle(e);
handled = true;
} catch (Exception ex) {
ex.addSuppressed(e);
ex.printStackTrace();
}
}
if (!handled) {
e.printStackTrace();
}
}
public Thread getThread() {
return T;
}
public void addInfoListener(VRClientRequestAppInfo l) {
infoListeners.add(l);
}
public void addMesh(Mesh m) {
toSend.add(m);
}
}
The Client2ServerCode is this:
package de.e_nexus.vr.server.codes;
public enum Client2ServerCode {
GET_APP_INFO, GET_INCOMING_MESH;
public static Client2ServerCode read(int read) {
for (Client2ServerCode c : values()) {
if (c.ordinal() == read) {
return c;
}
}
return null;
}
}
And this is my AGK Basic code
//******************************************************
// AGKVR Demo 1 - Castle Basic
//******************************************************
//This demo application will help you get to know AGKVR
SetErrorMode(0)
SetWindowTitle('VR')
//Load the AGKVR plugin
#import_plugin AGKVR
GET_APP_INFO as integer = 0
sock as integer
sock = ConnectSocket('127.0.0.1',8779,1000)
if sock = 0
message ('Timeout while connecting!')
end
endif
sleep(200)
if GetSocketConnected (sock) <> 1
message ('Not connected!')
end
endif
rem connected!
SendSocketByte(sock, GET_APP_INFO)
FlushSocket(sock)
if GetSocketConnected (sock) <> 1
message ('Could not request for app-info from server!')
end
endif
readTitle(sock)
DeleteSocket(sock)
sock = ConnectSocket('127.0.0.1',8779,1000)
if sock = 0
message ('Timeout while connecting!')
end
endif
readMesh(sock)
DeleteSocket(sock)
// <---------------------- below this is old example code, you can ignore it
//Init App
SetSyncRate(0, 0)
SetWindowSize( 1024, 768, 0 )
SetScissor(0, 0, 0, 0)
//Hand Model ID numbers
RightHandModel as integer = 100
LeftHandModel as integer = 101
HandModelImg as integer = 100
//Generate Mipmaps for nicer looking textures
SetGenerateMipmaps( 1 )
//Create Skybox
SetSkyBoxHorizonColor( 200, 200, 255 )
SetSkyBoxHorizonSize( 10, 2 )
SetSkyBoxSkyColor( 50, 50, 255)
SetSkyBoxSunColor( 255,255,255 )
SetSkyBoxVisible( 1 )
SetSkyBoxSunSize( 1, 3.0 )
SetSkyBoxSunVisible( 1 )
//Load Hand Models
//---------------------------
//Right Hand
LoadObjectWithChildren(RightHandModel,"RHand.FBX")
SetObjectScale(RightHandModel,1.15,1.15,1.15)
SetObjectPosition(RightHandModel,0.0,0.0,-0.07)
SetObjectRotation(RightHandModel,0.0,180.0,90.0)
FixObjectPivot(RightHandModel)
SetObjectAnimationSpeed(RightHandModel,20)
LoadImage(HandModelImg,"Hand.png")
SetObjectImage(RightHandModel,HandModelImg,0)
SetObjectVisible(RightHandModel,0)
SetObjectCollisionMode(RightHandModel,0)
//Left Hand
LoadObjectWithChildren(LeftHandModel,"LHand.FBX")
SetObjectScale(LeftHandModel,1.15,1.15,1.15)
SetObjectPosition(LeftHandModel,0.0,0.0,-0.07)
SetObjectRotation(LeftHandModel,0.0,180.0,270.0)
FixObjectPivot(LeftHandModel)
SetObjectAnimationSpeed(LeftHandModel,20)
SetObjectImage(LeftHandModel,HandModelImg,0)
SetObjectVisible(LeftHandModel,0)
SetObjectCollisionMode(LeftHandModel,0)
//Call function to load world models
LoadWorld( )
//Set the Camera Range in AGKVR
//It is necessary to use this command for setting the camera's range instead of the standard AGK SetCameraRange command
AGKVR.SetCameraRange( 0.01, 1000.0 )
//Initialiaze AGKVR
//------------------------------------------------
// The parameters are the Right and Left Eye image ID's that will be used to render to the HMD
InitError As Integer
RightEyeImg As Integer = 500
LeftEyeImg As Integer = 501
InitError = AGKVR.Init( RightEyeImg, LeftEyeImg )
// InitError = 0: SUCCESS!
// InitError = 1: Unable to init VR runtime
// InitError = 2: Compositor initialization failed.
//This command will lock the player's heading direction
//to follow the turn angle of the HMD. This would be common in FPS games, where
//you want the forward moving direction's turn angle to change based on where the
//player is looking. LockPlayerTurn is ON(1) by default
AGKVR.LockPlayerTurn( 1 )
//This command will lock the player's heading direction (forward on the Z axis)
//to follow the pitch angle of the HMD. This would be useful in a freeflight style game
//where you want the forward moving direction's pitch angle to change based on where the
//player is looking. LockPlayerPitch is OFF(0) by default
AGKVR.LockPlayerPitch( 0 )
CreateVector3(1,2,3.4)
do
//Get AGKVR's Player container's current Position, which will be used later for collision purposes:
PlayerPosX as float
PlayerPosY as float
PlayerPosZ as float
PlayerPosX = AGKVR.GetPlayerX(): `
PlayerPosY = AGKVR.GetPlayerY()
PlayerPosZ = AGKVR.GetPlayerZ()
//Control Player Turn Angle with Left Controller Joystick
valx as float
valz as float
valx = AGKVR.LeftController_JoyX( )
AGKVR.RotatePlayerLocalY( valx )
//Control Player Movement based on Right Controller Joystick
valx = AGKVR.RightController_JoyX( )
valz = AGKVR.RightController_JoyY( )
AGKVR.MovePlayerLocalZ( valz*0.06 )
AGKVR.MovePlayerLocalX( valx*0.06 )
//Move with the Keyboard
if GetRawKeyState(87) = 1
AGKVR.MovePlayerLocalZ( 0.1 )
endif
if GetRawKeyState(83) = 1
AGKVR.MovePlayerLocalZ( -0.1 )
endif
if GetRawKeyState(65) = 1
AGKVR.MovePlayerLocalX( -0.1 )
endif
if GetRawKeyState(68) = 1
AGKVR.MovePlayerLocalX( 0.1 )
endif
//Collision
New_PlayerPosX as float
New_PlayerPosY as float
New_PlayerPosZ as float
//Get the Player's ground position now that any movement has occured.
New_PlayerPosX = AGKVR.GetPlayerX()
New_PlayerPosY = AGKVR.GetPlayerY()
New_PlayerPosZ = AGKVR.GetPlayerZ()
//Check for collision from previous position to new position (add the radius to the Y position)
if ObjectSphereSlide(0,PlayerPosX,PlayerPosY+0.5,PlayerPosZ,New_PlayerPosX,New_PlayerPosY+0.5,New_PlayerPosZ,0.5)>0
//Get the collision point (Subtract the collision radius from the Y position)
New_PlayerPosX = GetObjectRayCastSlideX(0)
New_PlayerPosY = GetObjectRayCastSlideY(0)-0.5
New_PlayerPosZ = GetObjectRayCastSlideZ(0)
//Set the player position to the collision point
AGKVR.SetPlayerPosition( New_PlayerPosX, New_PlayerPosY, New_PlayerPosZ )
endif
//Control Hand animation based on Trigger feedback
Rvalue as float
Lvalue as float
Rvalue = AGKVR.RightController_Trigger( )
Lvalue = AGKVR.LeftController_Trigger( )
SetObjectAnimationFrame(RightHandModel, GetObjectAnimationName( RightHandModel, 1 ), GetObjectAnimationDuration(RightHandModel,GetObjectAnimationName( RightHandModel, 1 ))*Rvalue,0)
SetObjectAnimationFrame(LeftHandModel, GetObjectAnimationName( LeftHandModel, 1 ), GetObjectAnimationDuration(LeftHandModel,GetObjectAnimationName( LeftHandModel, 1 ))*Lvalue,0)
//Now that the player's position and orientation has been modified,
//the UpdatePlayer command is called to update all the components of
//AGKVR's player framework
AGKVR.UpdatePlayer( )
//Position Hand Objects
if AGKVR.RightControllerFound( ) = 1
SetObjectPosition( RightHandModel, AGKVR.GetRightHandX(), AGKVR.GetRightHandY(), AGKVR.GetRightHandZ())
SetObjectRotation( RightHandModel, AGKVR.GetRightHandAngleX(), AGKVR.GetRightHandAngleY(), AGKVR.GetRightHandAngleZ())
SetObjectVisible( RightHandModel, 1 )
else
SetObjectVisible( RightHandModel, 0 )
endif
if AGKVR.LeftControllerFound( ) = 1
SetObjectPosition( LeftHandModel, AGKVR.GetLeftHandX(), AGKVR.GetLeftHandY(), AGKVR.GetLeftHandZ())
SetObjectRotation( LeftHandModel, AGKVR.GetLeftHandAngleX(), AGKVR.GetLeftHandAngleY(), AGKVR.GetLeftHandAngleZ())
SetObjectVisible( LeftHandModel, 1 )
else
SetObjectVisible( LeftHandModel, 0 )
endif
//This command renders to the HMD.
AGKVR.Render( )
//The camera's position and rotation will determine what is displayed on the monitor, not the HMD
SetCameraPosition( 1, AGKVR.GetHMDX(), AGKVR.GetHMDY(), AGKVR.GetHMDZ())
SetCameraRotation( 1, AGKVR.GetHMDAngleX(), AGKVR.GetHMDAngleY(), AGKVR.GetHMDAngleZ())
Print( ScreenFPS() )
//Display some controller feedbacks on the monitor
if AGKVR.RightController_Grip() = 1
Print( "Right Grip Pressed" )
endif
if AGKVR.LeftController_Grip() = 1
Print( "Left Grip Pressed" )
endif
if AGKVR.LeftController_Button1() = 1
Print( "Left Button 1 Pressed" )
endif
if AGKVR.RightController_Button1() = 1
Print( "Right Button 1 Pressed" )
endif
if AGKVR.LeftController_Button2() = 1
Print( "Left Button 2 Pressed" )
endif
if AGKVR.RightController_Button2() = 1
Print( "Right Button 2 Pressed" )
endif
//Sync to update the monitor display (the HMD display is updated through AGKVR's Render Command
//so this Sync is not necessary to for rendering to the HMD)
Sync()
loop
function readTitle(s)
count as integer
count=0
sleep (5)
while count < 100
if GetSocketBytesAvailable(s) > 0
exit
endif
sleep(10)
inc count
endwhile
if count = 100
message ('Timeout while read title!')
end
endif
appTitleLength as integer
appTitleLength = GetSocketByte(s)
txt as string
txt=''
for i = 0 to appTitleLength
txt = txt + chr(GetSocketByte(s))
next i
SetWindowTitle(txt)
endfunction
function readMesh(s)
if SendSocketByte(s, 1) = 1
message('Could not place command to buffer')
end
endif
if FlushSocket(s) = 1
message('Could not send the buffered command to the server')
end
endif
count as integer
count = 0
message('flushed')
sleep (5)
while count < 100
if GetSocketBytesAvailable(s) > 0
exit
endif
sleep(4)
inc count
endwhile
if count = 100
message ('Timeout while read incomming mesh count!')
end
endif
// in count is the mesh size stored.
for i = 0 to count
lenSize as integer
count = 0
while count < 100
if GetSocketBytesAvailable(s) > 0
exit
endif
sleep(5)
inc count
endwhile
if count = 100
message('Timeout while read size of incomming mesh memblock.')
end
endif
lenSize = GetSocketByte(s)
lenTxt as String
lenTxt=''
for j = 0 to lenSize
lenTxt=lenTxt + chr(GetSocketByte(s))
next j
lenSize = val(lenTxt)
count = 0
while count < 100
if GetSocketBytesAvailable(s) = lenSize
exit
endif
sleep(5)
inc count
endwhile
if count = 100
message('Timeout while read incomming mesh memblock.')
end
endif
blk as integer
blk = CreateMemblock(lenSize)
for j = 0 to lenSize
SetMemblockByte(blk, j, GetSocketByte(s))
next j
CreateObjectFromMeshMemblock(blk)
next i
endfunction
function LoadWorld( )
scaleval as float = 0.037
LoadObject(1,"Castle.fbx")
SetObjectScale(1,scaleval,scaleval,scaleval)
LoadImage(1,"banquet hall_D.png")
LoadImage(2,"portcullis_D.png")
LoadImage(3,"stable_D.png")
LoadImage(4,"well_D.png")
LoadImage(5,"wall.png")
LoadImage(6,"tower.png")
LoadImage(7,"kings apartment_D.png")
SetObjectMeshImage( 1, 1, 2, 0 )
SetObjectMeshImage( 1, 2, 5, 0 )
SetObjectMeshImage( 1, 4, 6, 0 )
SetObjectMeshImage( 1, 5, 7, 0 )
SetObjectMeshImage( 1, 6, 1, 0 )
SetObjectMeshImage( 1, 7, 3, 0 )
SetObjectMeshImage( 1, 8, 4, 0 )
SetObjectMeshImage( 1, 9, 1, 0 )
//Field
LoadImage(50,"TerrainGrass.png")
LoadObject(50,"field.fbx")
SetObjectRotation(50,0,0,0)
SetObjectScale(50,5,5,5)
SetObjectImage(50,50,0)
SetObjectMeshUVScale( 50, 1, 0, 100, 100 )
SetImageWrapU(50,1)
SetImageWrapV(50,1)
endfunction
The App-info is read successfully but Java does not recieve the first byte of the second command, why not?
The socket is not allowed as parameter. I put the open socket inside the functions and it works.
Related
I am trying to make a Connect 5 game, where the game logic is held on the server side, with the client side influencing the current game state. So far, I have the game logic implemented and it works just fine if you were to run it. I am running into issues when trying to implement the actual client/server sider of things.
I am not exactly sure how to go about doing it. What I can do at the moment is get the player names and the size of the board. When it comes to actually playing the game, I run into some issues such as keeping the game running and getting the player's move. Currently the server will stop running after a short period of time. I have tried using a while(true) to keep it running but it doesn't seem to work.
Another issue is displaying the actual board on the client side - while I am able to display the board if you were to just play the game from the server class using System.out.println(fiveInARow);, which displays the board after every move. I have tried using How to send String array Object over Socket? to display the board (testing if I can even just get the empty board at the start of the game to display on the client side), I get an error.
Should I be doing something like How to get input from Console in Java Client Server Program to get an input from the user inside the for (int player = 0; moves-- > 0; player = 1 - player)?
UPDATED: So I'm able to make a move and the move will be played accordingly. However, the second player is unable to make a move (unable to enter input on client side after first input).
Server
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
class FiveInARow implements Runnable {
private ArrayList<String> playerNames = new ArrayList<String>();
private String currPlayer;
private Socket socket;
private Scanner scanner;
private int width, height;
private static final char[] PLAYERS = { 'X', 'O' };
private char[][] gameBoard;
private int lastCol = -1, lastRow = -1;
public FiveInARow(Socket socket) {
this.socket = socket;
}
/**
*
* #param w - Width
* #param h - Height
* #param p1 - Player 1
* #param p2 - Player 2
*/
public FiveInARow(int w, int h, String p1, String p2) {
width = w;
height = h;
gameBoard = new char[h][];
playerNames.add(p1);
playerNames.add(p2);
for (int i = 0; i < h; i++) {
Arrays.fill(gameBoard[i] = new char[w], '.');
}
}
// Display the game board
public String toString() {
return IntStream.range(0, width).mapToObj(Integer::toString).collect(Collectors.joining("")) + "\n"
+ Arrays.stream(gameBoard).map(String::new).collect(Collectors.joining("\n"));
}
// Get string representation of the row containing the last play of the user
public String horizontal() {
return new String(gameBoard[lastRow]);
}
// Get string representation of the column containing the last play of the user
public String vertical() {
StringBuilder stringBuilder = new StringBuilder(height);
for (int h = 0; h < height; h++) {
stringBuilder.append(gameBoard[h][lastCol]);
}
return stringBuilder.toString();
}
// Get string representation of the "/" diagonal containing the last play of the
// user
public String fowardSlashDiagonal() {
StringBuilder stringBuilder = new StringBuilder(height);
for (int h = 0; h < height; h++) {
int w = lastCol + lastRow - h;
if (w >= 0 && w < width) {
stringBuilder.append(gameBoard[h][w]);
}
}
return stringBuilder.toString();
}
/**
* Get string representation of the "\" diagonal containing the last play of the
* user
*
* #return
*/
public String backSlashDiagonal() {
StringBuilder stringBuilder = new StringBuilder(height);
for (int h = 0; h < height; h++) {
int w = lastCol - lastRow + h;
if (0 <= w && w < width) {
stringBuilder.append(gameBoard[h][w]);
}
}
return stringBuilder.toString();
}
public static boolean contains(String str, String subString) {
return str.indexOf(subString) >= 0;
}
// Determine if a game as been won
public boolean hasWon() {
if (lastCol == -1) {
System.err.println("No move has been made yet");
return false;
}
char symbol = gameBoard[lastRow][lastCol];
String streak = String.format("%c%c%c%c%c", symbol, symbol, symbol, symbol, symbol);
return contains(horizontal(), streak) || contains(vertical(), streak) || contains(fowardSlashDiagonal(), streak)
|| contains(backSlashDiagonal(), streak);
}
/**
*
* #param symbol - Symbol/piece to be played
* #param scanner - Input
*/
public void playMove(char symbol, Scanner scanner) {
do {
if (symbol == PLAYERS[0]) {
currPlayer = playerNames.get(0);
} else {
currPlayer = playerNames.get(1);
}
System.out.println("\n" + currPlayer + "'s turn: ");
int col = scanner.nextInt();
// Check if input is valid
if (!(0 <= col && col < width)) {
System.out.println("Column must be between 0 and " + (width - 1));
continue;
}
for (int h = height - 1; h >= 0; h--) {
if (gameBoard[h][col] == '.') {
gameBoard[lastRow = h][lastCol = col] = symbol;
return;
}
}
// If column has already been filled, we need to ask for a new input
System.out.println("Column " + col + " is full");
} while (true);
}
// public static void main(String[] args) {
// try (Scanner input = new Scanner(System.in)) {
// String player1Name, player2Name;
// int height = 6;
// int width = 9;
// int moves = height * width;
//
// System.out.println("Player 1 name: ");
// player1Name = input.next();
//
// System.out.println("Player 2 name: ");
// player2Name = input.next();
//
// FiveInARow fiveInARow = new FiveInARow(width, height, player1Name, player2Name);
//
// System.out.println("Enter 0 - " + (width - 1) + " to play a piece\n");
//
// System.out.println(fiveInARow);
//
// for (int player = 0; moves-- > 0; player = 1 - player) {
// char symbol = PLAYERS[player];
//
// fiveInARow.playMove(symbol, input);
//
// System.out.println(fiveInARow);
//
// if (fiveInARow.hasWon()) {
// System.out.println("\nPlayer " + symbol + " wins!");
//
// return;
// }
// }
//
// System.out.println("Game over. Draw game!");
// }
// }
#Override
public void run() {
int height = 6;
int width = 9;
int moves = height * width;
System.out.println("Connected: " + socket);
try {
FiveInARow fiveInARow = new FiveInARow(width, height, "Kevin", "Fasha");
Scanner scanner = new Scanner(socket.getInputStream());
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
// while (scanner.hasNextInt()) {
// printWriter.println(scanner.nextInt());
// }
System.out.println(fiveInARow);
// while (scanner.hasNextInt()) {
for (int player = 0; moves-- > 0; player = 1 - player) {
char symbol = PLAYERS[player];
fiveInARow.playMove(symbol, scanner);
System.out.println(fiveInARow);
if (fiveInARow.hasWon()) {
System.out.println("\nPlayer " + symbol + " wins!");
return;
}
// }
}
} catch (Exception exception) {
System.out.println("Error: " + socket);
} finally {
try {
socket.close();
} catch (IOException e) {
}
System.out.println("Closed: " + socket);
}
}
public static void main(String[] args) throws Exception {
try (ServerSocket serverSocket = new ServerSocket(59898)) {
System.out.println("The game server is running...");
ExecutorService pool = Executors.newFixedThreadPool(20);
while (true) {
pool.execute(new FiveInARow(serverSocket.accept()));
}
}
}
}
Client
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class FiveInARowClient {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Pass the server IP as the sole command line argument");
return;
}
try (Socket socket = new Socket(args[0], 59898)) {
System.out.println("Enter lines of text then Ctrl+D or Ctrl+C to quit");
Scanner scanner = new Scanner(System.in);
Scanner in = new Scanner(socket.getInputStream());
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
while (scanner.hasNextLine()) {
out.println(scanner.nextLine());
System.out.println(in.nextLine());
}
}
}
}
I'm working on a problem from Java How-to-Code, question 28.20. The book set up a multithreaded client/server to allow two users to play tic tac toe against each other. The problem asks us to modify the existing code to add certain features. The one that I am stuck on is to add a "New Game" button. I have added the button, and gotten the client that presses the button to notify the server, but what I can't figure out how to do, is have the server send a message to the other client. Right now, it will only respond back to the one that pressed the button. How can I get it to send a message to either the other or to both clients? Thanks.
Here is the client code:
// Fig. 27.15: TicTacToeClient.java
// Client side of client/server Tic-Tac-Toe program.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.Socket;
import java.net.InetAddress;
import java.io.IOException;
import javax.swing.*;
import java.util.Formatter;
import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class TicTacToeClient extends JFrame implements Runnable
{
private JTextField idField; // textfield to display player's mark
private JTextArea displayArea; // JTextArea to display output
private JPanel boardPanel; // panel for tic-tac-toe board
private JPanel panel2; // panel to hold board
private JButton playAgain;
private JButton disconnect;
private Square[][] board; // tic-tac-toe board
private Square currentSquare; // current square
private Socket connection; // connection to server
private Scanner input; // input from server
private Formatter output; // output to server
private String ticTacToeHost; // host name for server
private String myMark; // this client's mark
private boolean myTurn; // determines which client's turn it is
private final String X_MARK = "X"; // mark for first client
private final String O_MARK = "O"; // mark for second client
// set up user-interface and board
public TicTacToeClient( String host )
{
ticTacToeHost = host; // set name of server
displayArea = new JTextArea( 4, 30 ); // set up JTextArea
displayArea.setEditable( false );
add( new JScrollPane( displayArea ), BorderLayout.SOUTH );
boardPanel = new JPanel(); // set up panel for squares in board
boardPanel.setLayout( new GridLayout( 3, 3, 0, 0 ) );
board = new Square[ 3 ][ 3 ]; // create board
// loop over the rows in the board
for ( int row = 0; row < board.length; row++ )
{
// loop over the columns in the board
for ( int column = 0; column < board[ row ].length; column++ )
{
// create square
board[ row ][ column ] = new Square( " ", row * 3 + column );
boardPanel.add( board[ row ][ column ] ); // add square
} // end inner for
} // end outer for
idField = new JTextField(); // set up textfield
idField.setEditable( false );
add(idField, BorderLayout.NORTH);
panel2 = new JPanel(); // set up panel to contain boardPanel
panel2.add(boardPanel, BorderLayout.CENTER); // add board panel
add( panel2, BorderLayout.CENTER ); // add container panel
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BorderLayout(0,5));
playAgain = new JButton("Play again?");
playAgain.setFont(new Font("NewFont",Font.PLAIN,12));
playAgain.setEnabled(false);
buttonPanel.add(playAgain, BorderLayout.NORTH);
disconnect = new JButton("Disconnect");
disconnect.setFont(new Font("NewFont2", Font.PLAIN, 12));
buttonPanel.add(disconnect, BorderLayout.SOUTH);
add(buttonPanel, BorderLayout.EAST);
setSize( 350, 250 ); // set size of window
setVisible( true ); // show window
startClient();
} // end TicTacToeClient constructor
// start the client thread
public void startClient()
{
try // connect to server and get streams
{
// make connection to server
connection = new Socket(
InetAddress.getByName( ticTacToeHost ), 12345 );
// get streams for input and output
input = new Scanner( connection.getInputStream() );
output = new Formatter( connection.getOutputStream() );
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
} // end catch
// create and start worker thread for this client
ExecutorService worker = Executors.newFixedThreadPool( 1 );
worker.execute( this ); // execute client
} // end method startClient
// control thread that allows continuous update of displayArea
public void run()
{
myMark = input.nextLine(); // get player's mark (X or O)
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
// display player's mark
idField.setText( "You are player \"" + myMark + "\"" );
} // end method run
} // end anonymous inner class
); // end call to SwingUtilities.invokeLater
myTurn = ( myMark.equals( X_MARK ) ); // determine if client's turn
playAgain.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
output.format("New Game\n");
output.flush();
// loop over the rows in the board
for ( int row = 0; row < board.length; row++ )
{
// loop over the columns in the board
for ( int column = 0; column < board[ row ].length;column++ )
{
// create square
setMark(board[row][column], "");
} // end inner for
} // end outer for
}
});
// receive messages sent to client and output them
while ( true )
{
if ( input.hasNextLine() )
processMessage( input.nextLine() );
} // end while
} // end method run
// process messages received by client
private void processMessage( String message )
{
// valid move occurred
if ( message.equals( "Valid move." ) )
{
displayMessage( "Valid move, please wait.\n" );
setMark( currentSquare, myMark ); // set mark in square
} // end if
else if ( message.equals( "Invalid move, try again" ) )
{
displayMessage( message + "\n" ); // display invalid move
myTurn = true; // still this client's turn
} // end else if
else if ( message.equals( "Opponent moved" ) )
{
int location = input.nextInt(); // get move location
input.nextLine(); // skip newline after int location
int row = location / 3; // calculate row
int column = location % 3; // calculate column
setMark( board[ row ][ column ],
( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // mark move
displayMessage( "Opponent moved. Your turn.\n" );
myTurn = true; // now this client's turn
} // end else if
else if(message.equals("Game over. Players Tied")){
int location = input.nextInt(); // get move location
input.nextLine(); // skip newline after int location
int row = location / 3; // calculate row
int column = location % 3; // calculate column
setMark( board[ row ][ column ],
( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // mark move
displayMessage("Game over. Players tied");
playAgain.setEnabled(true);
}//else if
else if(message.equals("Game over, you lose")){
int location = input.nextInt(); // get move location
input.nextLine(); // skip newline after int location
int row = location / 3; // calculate row
int column = location % 3; // calculate column
setMark( board[ row ][ column ],
( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // mark move
displayMessage("Game over, you lose.");
playAgain.setEnabled(true);
}//else if
else if(message.equals("Game over. You win!")){
displayMessage("Game over. You win!");
playAgain.setEnabled(true);
}else if(message.equals("New Game")){
displayMessage("\nNew game requested");
// loop over the rows in the board
for ( int row = 0; row < board.length; row++ )
{
// loop over the columns in the board
for ( int column = 0; column < board[ row ].length; column++ )
{
// create square
setMark(board[row][column], "");
} // end inner for
} // end outer for
playAgain.setEnabled(false);
}
else
displayMessage( message + "\n" ); // display the message
} // end method processMessage
// manipulate displayArea in event-dispatch thread
private void displayMessage( final String messageToDisplay )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
displayArea.append( messageToDisplay ); // updates output
} // end method run
} // end inner class
); // end call to SwingUtilities.invokeLater
} // end method displayMessage
// utility method to set mark on board in event-dispatch thread
private void setMark( final Square squareToMark, final String mark )
{
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
squareToMark.setMark( mark ); // set mark in square
} // end method run
} // end anonymous inner class
); // end call to SwingUtilities.invokeLater
} // end method setMark
// send message to server indicating clicked square
public void sendClickedSquare( int location )
{
// if it is my turn
if ( myTurn )
{
output.format( "%d\n", location ); // send location to server
output.flush();
myTurn = false; // not my turn any more
} // end if
} // end method sendClickedSquare
// set current Square
public void setCurrentSquare( Square square )
{
currentSquare = square; // set current square to argument
} // end method setCurrentSquare
// private inner class for the squares on the board
private class Square extends JPanel
{
private String mark; // mark to be drawn in this square
private int location; // location of square
public Square( String squareMark, int squareLocation )
{
mark = squareMark; // set mark for this square
location = squareLocation; // set location of this square
addMouseListener(
new MouseAdapter()
{
public void mouseReleased( MouseEvent e )
{
setCurrentSquare( Square.this ); // set current
// send location of this square
sendClickedSquare( getSquareLocation() );
} // end method mouseReleased
} // end anonymous inner class
); // end call to addMouseListener
} // end Square constructor
// return preferred size of Square
public Dimension getPreferredSize()
{
return new Dimension( 30, 30 ); // return preferred size
} // end method getPreferredSize
// return minimum size of Square
public Dimension getMinimumSize()
{
return getPreferredSize(); // return preferred size
} // end method getMinimumSize
// set mark for Square
public void setMark( String newMark )
{
mark = newMark; // set mark of square
repaint(); // repaint square
} // end method setMark
// return Square location
public int getSquareLocation()
{
return location; // return location of square
} // end method getSquareLocation
// draw Square
public void paintComponent( Graphics g )
{
super.paintComponent( g );
g.drawRect( 0, 0, 29, 29 ); // draw square
g.drawString( mark, 11, 20 ); // draw mark
} // end method paintComponent
} // end inner-class Square
} // end class TicTacToeClient
the server code
// Fig. 27.13: TicTacToeServer.java
// Server side of client/server Tic-Tac-Toe program.
import java.awt.BorderLayout;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.util.Formatter;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TicTacToeServer extends JFrame
{
private String[] board = new String[ 9 ]; // tic-tac-toe board
private JTextArea outputArea; // for outputting moves
private Player[] players; // array of Players
private ServerSocket server; // server socket to connect with clients
private int currentPlayer; // keeps track of player with current move
private final static int PLAYER_X = 0; // constant for first player
private final static int PLAYER_O = 1; // constant for second player
private final static String[] MARKS = { "X", "O" }; // array of marks
private ExecutorService runGame; // will run players
private Lock gameLock; // to lock game for synchronization
private Condition otherPlayerConnected; // to wait for other player
private Condition otherPlayerTurn; // to wait for other player's turn
// set up tic-tac-toe server and GUI that displays messages
public TicTacToeServer()
{
super( "Tic-Tac-Toe Server" ); // set title of window
// create ExecutorService with a thread for each player
runGame = Executors.newFixedThreadPool( 2 );
gameLock = new ReentrantLock(); // create lock for game
// condition variable for both players being connected
otherPlayerConnected = gameLock.newCondition();
// condition variable for the other player's turn
otherPlayerTurn = gameLock.newCondition();
for ( int i = 0; i < 9; i++ )
board[ i ] = ""; // create tic-tac-toe board
players = new Player[ 2 ]; // create array of players
currentPlayer = PLAYER_X; // set current player to first player
try
{
server = new ServerSocket( 12345, 2 ); // set up ServerSocket
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
System.exit( 1 );
} // end catch
outputArea = new JTextArea(); // create JTextArea for output
add( outputArea, BorderLayout.CENTER );
outputArea.setText( "Server awaiting connections\n" );
setSize( 300, 300 ); // set size of window
setVisible( true ); // show window
} // end TicTacToeServer constructor
// wait for two connections so game can be played
public void execute()
{
// wait for each client to connect
for ( int i = 0; i < players.length; i++ )
{
try // wait for connection, create Player, start runnable
{
players[ i ] = new Player( server.accept(), i );
runGame.execute( players[ i ] ); // execute player runnable
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
System.exit( 1 );
} // end catch
} // end for
gameLock.lock(); // lock game to signal player X's thread
try
{
players[ PLAYER_X ].setSuspended( false ); // resume player X
otherPlayerConnected.signal(); // wake up player X's thread
} // end try
finally
{
gameLock.unlock(); // unlock game after signalling player X
} // end finally
} // end method execute
// display message in outputArea
private void displayMessage( final String messageToDisplay )
{
// display message from event-dispatch thread of execution
SwingUtilities.invokeLater(
new Runnable()
{
public void run() // updates outputArea
{
outputArea.append( messageToDisplay ); // add message
} // end method run
} // end inner class
); // end call to SwingUtilities.invokeLater
} // end method displayMessage
// determine if move is valid
public boolean validateAndMove( int location, int player )
{
// while not current player, must wait for turn
while ( player != currentPlayer )
{
gameLock.lock(); // lock game to wait for other player to go
try
{
otherPlayerTurn.await(); // wait for player's turn
} // end try
catch ( InterruptedException exception )
{
exception.printStackTrace();
} // end catch
finally
{
gameLock.unlock(); // unlock game after waiting
} // end finally
} // end while
// if location not occupied, make move
if ( !isOccupied( location ) )
{
board[ location ] = MARKS[ currentPlayer ]; // set move on board
currentPlayer = 1- currentPlayer; // change player
// let new current player know that move occurred
players[ currentPlayer ].otherPlayerMoved( location );
gameLock.lock(); // lock game to signal other player to go
try
{
if(!isWin() && !isTie())
otherPlayerTurn.signal(); // signal other player to continue
} // end try
finally
{
if(!isWin() && !isTie())
gameLock.unlock(); // unlock game after signaling
} // end finally
return true; // notify player that move was valid
} // end if
else // move was not valid
return false; // notify player that move was invalid
} // end method validateAndMove
// determine whether location is occupied
public boolean isOccupied( int location )
{
if ( board[ location ].equals( MARKS[ PLAYER_X ] ) ||
board [ location ].equals( MARKS[ PLAYER_O ] ) )
return true; // location is occupied
else
return false; // location is not occupied
} // end method isOccupied
// place code in this method to determine whether game over
public boolean isWin()
{
boolean isGameOver = false;
if(!board[0].equals("") && board[0].equals(board[1])
&& board[1].equals(board[2]))
isGameOver = true;
if(!board[3].equals("") && board[3].equals(board[4])
&& board[4].equals(board[5]))
isGameOver = true;
if(!board[6].equals("") && board[6].equals(board[7])
&& board[7].equals(board[8]))
isGameOver = true;
if(!board[0].equals("") && board[0].equals(board[3])
&& board[3].equals(board[6]))
isGameOver = true;
if(!board[1].equals("") && board[1].equals(board[4])
&& board[4].equals(board[7]))
isGameOver = true;
if(!board[2].equals("") && board[2].equals(board[5])
&& board[5].equals(board[8]))
isGameOver = true;
if(!board[0].equals("") && board[0].equals(board[4])
&& board[4].equals(board[8]))
isGameOver = true;
if(!board[2].equals("") && board[2].equals(board[4])
&& board[4].equals(board[6]))
isGameOver = true;
return isGameOver; // this is left as an exercise
} // end method isWin
public boolean isTie(){
boolean isTie = false;
if(!board[0].equals("") && !board[1].equals("")
&& !board[2].equals("") && !board[3].equals("") && !board[4].equals("")
&& !board[5].equals("") && !board[6].equals("")
&& !board[7].equals("") && !board[8].equals(""))
isTie = true;
return isTie;
}//end method is Tie
// private inner class Player manages each Player as a runnable
private class Player implements Runnable
{
private Socket connection; // connection to client
private Scanner input; // input from client
private Formatter output; // output to client
private int playerNumber; // tracks which player this is
private String mark; // mark for this player
private boolean suspended = true; // whether thread is suspended
// set up Player thread
public Player( Socket socket, int number )
{
playerNumber = number; // store this player's number
mark = MARKS[ playerNumber ]; // specify player's mark
connection = socket; // store socket for client
try // obtain streams from Socket
{
input = new Scanner( connection.getInputStream() );
output = new Formatter( connection.getOutputStream() );
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
System.exit( 1 );
} // end catch
} // end Player constructor
// send message that other player moved
public void otherPlayerMoved( int location )
{
if (!isTie() && !isWin()) {
output.format("Opponent moved\n");
output.format("%d\n", location); // send location of move
output.flush(); // flush output
}else if(isWin()){
output.format("Game over, you lose\n");
output.format("%d\n", location);
output.flush();
}else if( isTie()){
output.format("Game over. Players Tied\n");
output.format("%d\n", location);
output.flush();
}
} // end method otherPlayerMoved
// control thread's execution
public void run()
{
// send client its mark (X or O), process messages from client
try
{
displayMessage( "Player " + mark + " connected\n" );
output.format( "%s\n", mark ); // send player's mark
output.flush(); // flush output
// if player X, wait for another player to arrive
if ( playerNumber == PLAYER_X )
{
output.format( "%s\n%s", "Player X connected",
"Waiting for another player\n" );
output.flush(); // flush output
gameLock.lock(); // lock game to wait for second player
try
{
while( suspended )
{
otherPlayerConnected.await(); // wait for player O
} // end while
} // end try
catch ( InterruptedException exception )
{
exception.printStackTrace();
} // end catch
finally
{
gameLock.unlock(); // unlock game after second player
} // end finally
// send message that other player connected
output.format( "Other player connected. Your move.\n" );
output.flush(); // flush output
} // end if
else
{
output.format( "Player O connected, please wait\n" );
output.flush(); // flush output
} // end else
// while game not over
while ( true )
{
int location = 9; // initialize move location
if ( input.hasNextInt() )
location = input.nextInt(); // get move location
if ( input.hasNextLine() )
processMessage( input.nextLine() );
// check for valid move
if ( validateAndMove( location, playerNumber ) )
{
displayMessage( "\nlocation: " + location );
output.format( "Valid move.\n" ); // notify client
output.flush(); // flush output
if (isWin()){
output.format("Game over. You win!\n");
//output.format("%d\n", location);
output.flush();
}else if (isTie()){
output.format("Game over. Players Tied\n");
output.format("%d\n", location);
output.flush();
}
} // end if
else // move was invalid
{
output.format( "Invalid move, try again\n" );
output.flush(); // flush output
} // end else
} // end while
} // end try
finally
{
try
{
connection.close(); // close connection to client
} // end try
catch ( IOException ioException )
{
ioException.printStackTrace();
System.exit( 1 );
} // end catch
} // end finally
} // end method run
// set whether or not thread is suspended
public void setSuspended( boolean status )
{
suspended = status; // set value of suspended
} // end method setSuspended
public void processMessage(String message){
if ( message.equals( "New Game" ) )
{
displayMessage( "\nPlayer requested new game" );
for(int i=0; i<board.length; i++){
board[i] = "";
}
output.format("New Game\n");
output.flush();
} // end if
}
} // end class Player
} // end class TicTacToeServer
Your clients will need to listen to the server. Ideally, the action messages sent by the clients would be independent of the status messages sent by the server. This decoupling allows more robust scenarios than the "each client takes a turn" one you are doing. Specifically, the server would send status messages to all the clients, and the clients would update their state based on those messages, rather than their own actions.
When the requestTime == 0 I get 2 results. I've printed "luck" into the for loop and it loops for 5 seconds but the thing is when it reaches 0 it prints two results. I get one result from the System.out which is in the if(requestTime == 0 ) then my for loop runs 1 more and I get another System.out (an updated one) from if(requestTime == 0 ) How can I fix this? I need to get the FINAL value. A friend told me I could use arrays but I have no idea how. I need to calculate inside the for loop and print the SUM of the values.
//TIMER METHOD
static int requestTime = 5;
public static void randomN () {
myTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if(requestTime > 0)
{
requestTime--;
}
else myTimer.cancel();
int nTimesRandom = (int) (Math.random()*6+1);
for (int i=0; i<nTimesRandom; i++) {
int rx = (int) (Math.random()*10+1);
int ry = (int) (Math.random()*10+1);
a = a+nTimesRandom;
if (Math.sqrt(Math.pow((a1x1-rx),2)+Math.pow((a1y1-ry),2))<3) {
reached1++;
if ( cap1 < 3 ) { cap1++; }
else if ( cap1 == 3 ) { nreached1++; }
}
else { oor++; }
if (Math.sqrt(Math.pow((a2x2-rx),2)+Math.pow((a2y2-ry),2))<3) {
reached2++;
if ( cap2 < 3 ) { cap2++; }
else if ( cap2 == 3 ) { nreached2++; }
}
else { oor++; }
if (Math.sqrt(Math.pow((a3x3-rx),2)+Math.pow((a3y3-ry),2))<3) {
reached3++;
if ( cap3 < 3 ) { cap3++; }
else if ( cap3 == 3 ) { nreached3++; }
}
else { oor++; }
System.out.println("luck");
}
oor = a -( reached1+reached2+reached3) ;
int totalreached = reached1+reached2+reached3 ;
if ( requestTime == 0 ) {
System.out.println( a + " communication requests were generated, " + totalreached + " of them were served, " + oor + " of them were unserved."); }
// System.out.println("Requests " + "X : " + rx + " Y: " + ry);
}
}, 0, 1000);
}
My console output is like this :
...
luck
...
142 communication requests were generated, 20 of them were served, 122 of them were unserved.
luck
luck
luck
luck
158 communication requests were generated, 23 of them were served, 135 of them were unserved.
If the request time is > 0, then you decrement requestTime, and then execute the rest of the method. So when requestTime is 1, it becomes 0, and then the rest of the code is executed.
If the request time is == 0, then you don't decrement requestTime, and then execute the rest of the method. So it stays at 0, and then the rest of the code is executed.
I guess that you either want to decrement always, or to avoid the execution of the rest of the code if requestTime is == 0.
I am programming a boggle game board solver. It uses a Stack, a 2D Array Grid of letters for the game board that are read in from a .dat/.txt file, and Search "States" to store where its been, specifically (point coordinates, word so far). It is designed to search every possible letter combination on the board to form strings of length 3 or more, and then checks a dictionary file to see if that word is a valid boggle word solution. After, it stores the word and returns a list of solutions to the game board that was given in the arguments.
My Problem: For some reason this program has eluded me like no other thing I've ever written before. I am very new to the concept of "states" so that could be an underlying issue. I believe that what I have is fairly close to working, I am just at a loss for what could be wrong with it. The current issue is that it is not ever storing the current letter and building strings as it checks neighboring letters. It does check neighbors properly, but no strings are built. Here is the code:
BoggleSearch:
contains the main method, acts as the driver class.
import java.io.*;
import java.util.*;
public class BoggleSearch {
protected static int GRID_SIZE = 4;
public static String[][] grid = new String[GRID_SIZE][GRID_SIZE];
public static void main(String[] args) throws FileNotFoundException {
if (args.length != 1) {
System.err.println("Usage: java BoggleSearch gridFile");
System.exit(1);
}
Scanner scan = new Scanner(new File(args[0]));
String bigString = scan.next();
bigString = bigString+scan.next();
bigString = bigString+scan.next();
bigString = bigString+scan.next();
scan.close();
int count = 0;
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
grid[i][j] = bigString.substring(count, count);
count++;
}
}
WordSearch ws = new WordSearch(grid);
ArrayList<BoggleSearchState> foundWords = ws.startSearch();
System.out.println(foundWords);
}
}
WordSearch:
contains all of the algorithms that find that string every possible combination of letters possible on the given game board and cross-check them with the dictionary class.
import java.awt.Point;
import java.util.*;
public class WordSearch {
public static Stack<BoggleSearchState> stack;
public static ArrayList<BoggleSearchState> foundWords;
private String[][] grid;
private static final int GRID_SIZE = 4;
public BoggleDictionary dictionary;
public WordSearch(String[][] inputGrid) {
grid = new String[GRID_SIZE][GRID_SIZE];
stack = new Stack<BoggleSearchState>();
foundWords = new ArrayList<BoggleSearchState>();
inputGrid = new String[GRID_SIZE][GRID_SIZE];
try {
dictionary = new BoggleDictionary();
} catch (Exception e) {
System.err.println("blew up while making dict object");
e.printStackTrace();
}
}
public ArrayList<BoggleSearchState> startSearch() {
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid.length; j++) {
BoggleSearchState b = new BoggleSearchState(
new ArrayList<Point>(), grid[i][j]);
Point p = new Point(i, j);
b.path.add(p);
stack.push(b);
while (!stack.isEmpty()) {
BoggleSearchState s = stack.pop();
if (s.getWord().length() >=1 && dictionary.contains(s.getWord())) {
foundWords.add(s);
}
Point loc = s.path.get(s.path.size() - 1);
p = new Point(loc.x,loc.y);
// Bottom Neighbor
if (loc.x + 1 >= 0 && loc.x + 1 < grid.length && loc.y >= 0
&& loc.y < grid.length) {
if (s.getVisited(new Point(p.x+1,p.y)) != true) {
BoggleSearchState neo = new BoggleSearchState(new ArrayList<Point>(),s.getWord() + grid[loc.x + 1][loc.y]);
neo.path.add(new Point(p.x+1,p.y));
stack.push(neo);
}
}
// Top Neighbor
if (loc.x - 1 >= 0 && loc.x - 1 < grid.length && loc.y >= 0
&& loc.y < grid.length) {
if (s.getVisited(new Point(p.x-1,p.y)) != true) {
BoggleSearchState neo = new BoggleSearchState(
new ArrayList<Point>(),s.getWord() +
grid[loc.x - 1][loc.y]);
neo.path.add(new Point(p.x-1,p.y));
stack.push(neo);
}
}
// Right Neighbor
if (loc.x >= 0 && loc.x < grid.length && loc.y + 1 >= 0
&& loc.y + 1 < grid.length) {
if (s.getVisited(new Point(p.x,p.y+1)) != true) {
BoggleSearchState neo = new BoggleSearchState(
new ArrayList<Point>(),s.getWord() +
grid[loc.x][loc.y + 1]);
neo.path.add(new Point(p.x,p.y+1));
stack.push(neo);
}
}
// Left Neighbor
if (loc.x >= 0 && loc.x < grid.length && loc.y - 1 >= 0
&& loc.y - 1 < grid.length) {
if (s.getVisited(new Point(p.x,p.y-1)) != true) {
BoggleSearchState neo = new BoggleSearchState(
new ArrayList<Point>(),s.getWord() +
grid[loc.x][loc.y - 1]);
neo.path.add(new Point(p.x,p.y-1));
stack.push(neo);
}
}
// Bottom-Right Neighbor
if (loc.x + 1 >= 0 && loc.x + 1 < grid.length
&& loc.y + 1 >= 0 && loc.y + 1 < grid.length) {
if (s.getVisited(new Point(p.x+1,p.y+1)) != true) {
BoggleSearchState neo = new BoggleSearchState(
new ArrayList<Point>(),s.getWord() +
grid[loc.x + 1][loc.y + 1]);
neo.path.add(new Point(p.x+1,p.y+1));
stack.push(neo);
}
}
// Bottom-Left Neighbor
if (loc.x + 1 >= 0 && loc.x + 1 < grid.length
&& loc.y - 1 >= 0 && loc.y - 1 < grid.length) {
if (s.getVisited(new Point(p.x+1,p.y-1)) != true) {
BoggleSearchState neo = new BoggleSearchState(
new ArrayList<Point>(),s.getWord() +
grid[loc.x + 1][loc.y - 1]);
neo.path.add(new Point(p.x+1,p.y-1));
stack.push(neo);
}
}
// Top-Right Neighbor
if (loc.x - 1 >= 0 && loc.x - 1 < grid.length
&& loc.y + 1 >= 0 && loc.y + 1 < grid.length) {
if (s.getVisited(new Point(p.x-1,p.y+1)) != true) {
BoggleSearchState neo = new BoggleSearchState(
new ArrayList<Point>(),s.getWord() +
grid[loc.x - 1][loc.y + 1]);
neo.path.add(new Point(p.x-1,p.y+1));
stack.push(neo);
}
}
// Top-Left Neighbor
if (loc.x - 1 >= 0 && loc.x - 1 < grid.length
&& loc.y - 1 >= 0 && -1 < grid.length) {
if (s.getVisited(new Point(p.x-1,p.y-1)) != true) {
BoggleSearchState neo = new BoggleSearchState(
new ArrayList<Point>(),s.getWord() +
grid[loc.x - 1][loc.y - 1]);
neo.path.add(new Point(p.x-1,p.y-1));
stack.push(neo);
}
}
}
}
}
return foundWords;
}
}
BoggleSearchState:
Creates a state object that is used to store necessary data for each instance of a string forming path on the game board. contains methods necessary for its purpose.
import java.awt.Point;
import java.util.ArrayList;
public class BoggleSearchState {
private String word="";
public ArrayList<Point> path = new ArrayList<Point>();
public BoggleSearchState(ArrayList<Point>path, String word) {
this.path = path;
this.word = word;
}
public String getWord() {
return word;
}
public ArrayList<Point> getLocation() {
return path;
}
public boolean getVisited (Point p) {
ArrayList<Point> newPath = new ArrayList<Point>();
for (Point s: path) {
newPath.add(s);
if (p.equals(s)) {
return true;
}
}
return false;
}
public String toString() {
return this.word;
}
}
BoggleDictionary:
The terribly written dictionary class that was given for the assignment. It is tested and fully functional however.
// BoggleDictionary.java
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.util.Scanner;
import java.util.HashSet;
import java.util.Iterator;
/**
A class that stores a dictionary containing words that can be used in a
Boggle game.
#author Teresa Cole
#version CS221 Fall 2013
*/
public class BoggleDictionary
{
private HashSet<String> dictionary;
/** Create the BoggleDictionary from the file dictionary.dat
*/
#SuppressWarnings("unchecked")
public BoggleDictionary() throws Exception {
ObjectInputStream dictFile = new ObjectInputStream(
new FileInputStream( new File( "dictionary.dat")));
dictionary = (HashSet<String>)dictFile.readObject();
dictFile.close();
}
/** Check to see if a string is in the dictionary to determine whether it
* is a valid word.
* #param word the string to check for
* #return true if word is in the dictionary, false otherwise.
*/
public boolean contains( String word)
{
return dictionary.contains( word);
}
/** Get an iterator that returns all the words in the dictionary, one at a
* time.
* #return an iterator that can be used to get all the words in the
* dictionary.
*/
public Iterator<String> iterator()
{
return dictionary.iterator();
}
/**
Main entry point
*/
static public void main(String[] args)
{
System.out.println( "BoggleDictionary Program ");
Scanner kbd = new Scanner( System.in);
BoggleDictionary theDictionary=null;
try
{
theDictionary = new BoggleDictionary();
}
catch (Exception ioe)
{
System.err.println( "error reading dictionary");
System.exit(1);
}
String word;
/*
while (kbd.hasNext())
{
word = kbd.next();
if (theDictionary.contains( word))
System.out.println( word + " is in the dictionary");
else
System.out.println( word + " is not in the dictionary");
}
*/
Iterator<String> iter = theDictionary.iterator();
while (iter.hasNext())
System.out.println( iter.next());
}
}
I would appreciate any help on this as I am really struggling with it at this point. I understand that there are numerous ways to implement other data structures or organization methods to accomplish this task in a much more efficient run-time. However the concern of the assignment is not catered towards efficiency, rather the underlying principles of using these data structures (stacks etc.) and understanding how you are able to go a wrong direction and then safely back track and go a new direction without the program crashing. Thank you in advance, any questions I'll try to answer as fast as I can.
As near as I can tell, you have two grids in WordSearch, but you set both of them to initialized arrays. You never use the grid you look like you might have built in the main method.
But it is hard to tell.
You have given us much data but little information. We don't need details about the entirety of a program, even one this size; we need to know what your specific question is. No one is liable to debug this for you, and in fact few enough are going to read as much of it as I have.
After you've fixed your initialization problem, debug your program however you can and figure out where it does something that (1) it isn't supposed to, and (2) you don't understand. You need to spend enough time attempting to figure out #1 yourself for your own education on debugging, and it makes it much more likely that you can explain what you don't understand and get a good answer to a specific question. "It isn't Building Strings" doesn't go quite far enough; WHERE isn't it building, what do you mean by building strings, etc. I expect it's because it has no input, but I didn't analyze that far.
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);
}