📄 clientplayer.java
字号:
package Client;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;
import Utility.*;
/* ClientPlayer a subclass of JApplet implements the client side of the battle ship game using a separate thread.
The ClientPlayer though a subclass of JApplet is run as a stand-alone application here (as the boolean variable
isStandAlone is set to true). The JFrame and JApple classes are both subclasses of Container and have the same
layout managers, user interface components and event handling.
The Web browser creates an applet using the no-arg constructor and controls the applet using the init(), start(), stop() and destroy()
methods. Note in the main() method we have called the init() method followed by start() method.
Thus this program can be made to run as an applet
The init() method adds two Buttons to position ships and to start shooting (initially made inactive).
After this it calls connectToServer to start a separate thread that interacts with server.
*/
public class ClientPlayer extends JApplet implements Runnable, ActionListener, BattleShipConstants
{
private boolean entered = false;
private boolean myTurn = false;
private boolean positioned = false;
private int lastShot;
private Board ourBoard;
private Board enemyBoard;
private JPanel controlPanel;
JButton positionButton;
JButton startButton;
NavalForce nf = new NavalForce();
private DataInputStream fromServer;
private DataOutputStream toServer;
private boolean isStandAlone = true;
private String host = "localhost";
public static void main(String args[])
{
JFrame frame = new JFrame("Battleship");
ClientPlayer p = new ClientPlayer();
frame.getContentPane().add(p,BorderLayout.CENTER);
p.init(); // calls the init() method (of this Applet derived clss)
p.start(); // starts this applet running
frame.setSize(320,600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void init()
{
getContentPane().setLayout(new BorderLayout());
enemyBoard = new Board("Enemy Board", this, true); // true -> the buttons active
this.getContentPane().add(enemyBoard,BorderLayout.CENTER);
ourBoard = new Board("Our Board", this, false); // false -> the buttons inactive
this.getContentPane().add(ourBoard,BorderLayout.SOUTH);
controlPanel = new JPanel();
controlPanel.setLayout(new GridLayout(1,5,5,5));
positionButton = new JButton("Position Ships");
controlPanel.add(positionButton);
controlPanel.setBorder(new TitledBorder("Controls"));
this.getContentPane().add(controlPanel,BorderLayout.NORTH);
positionButton.addActionListener(this);
startButton = new JButton("Start Shooting now");
startButton.setVisible(false);
startButton.addActionListener(this);
controlPanel.add(startButton);
connectToServer();
}
/* A simple interface to position ships
Checks for Ship overlaps and ensures ships lie within the grid 10 x 10 cells
Note immediately after positioning ships, myTurn is set to true
*/
public void positionShips()
{
for (int i=1; i<=5; i++)
{
for (int j=1; j<=2; j++)
{
boolean okay = false;
do {
try {
String sel = JOptionPane.showInputDialog(null,"Enter pos(1-100) and dir (h/v) of " + shipTypes[i-1]+j+
"\ni.e. 75 v","Input Ship", JOptionPane.QUESTION_MESSAGE);
StringTokenizer st = new StringTokenizer(sel);
if ( st.countTokens() != 2) throw new Exception("Invalid number of argument");
int topLeftPos = Integer.parseInt(st.nextToken());
if ( topLeftPos < 0 || topLeftPos > 100) throw new Exception("pos outside range");
String direction = st.nextToken();
char ch = direction.charAt(0);
if ( ch != 'H' && ch != 'h' && ch != 'v' && ch != 'V')
throw new Exception("Invalid direction");
okay = false;
int orientation = ch=='H' || ch == 'h' ? 0 : 1;
Ship s = new Ship(i,orientation,topLeftPos);
nf.add(s);
ourBoard.display(s);
okay = true;
}
catch ( ShipException se)
{ JOptionPane.showMessageDialog(null,se.toString(),"Invalid Ship Position",JOptionPane.WARNING_MESSAGE);
}
catch ( Exception se)
{ JOptionPane.showMessageDialog(null,se.toString(),"Invalid inout",JOptionPane.WARNING_MESSAGE);
}
}while (!okay);
}
}
positionButton.setEnabled(false);
positioned = true;
startButton.setVisible(true);
startButton.setEnabled(false);
}
public synchronized void actionPerformed(ActionEvent e)
{
if ( e.getSource() == positionButton)
positionShips();
else if (myTurn) // allowed to shoot only when it is myTurn
shoot(e);
}
/* When the user clicks an active button we save the position
and set entered to true and myTurn to false.
This method also updates enemy Board */
public synchronized void shoot(ActionEvent e)
{
int pos = Integer.parseInt(((JButton)e.getSource()).getText());
lastShot = pos;
myTurn = false;
entered = true;
notifyAll();
enemyBoard.setCross(pos);
}
/* This method waits until user shoots by pressing an active button (entered == true)
It then writes it to the socket (together with a header)
and sets entered to false; */
public synchronized void bombAtEnemy()
{
myTurn = true;
try {
while (!entered) wait(); //Thread.sleep(100);
toServer.writeInt(BOMBED_AT);
toServer.writeInt(lastShot);
entered = false;
}
catch(Exception e) {}
}
public void connectToServer()
{
try {
Socket socket;
if (isStandAlone)
socket = new Socket(host,8000);
else
socket = new Socket(getCodeBase().getHost(), 8000);
fromServer = new DataInputStream(socket.getInputStream());
toServer = new DataOutputStream(socket.getOutputStream());
}
catch(Exception ex) {
System.err.println(ex);
}
Thread thread = new Thread(this);
thread.start();
}
/* This method implements the protocol using the Damage class
if the server is responding to the previous shot with a hit we get to shoot again
after reflecting the damages in the enemyBoard. We win if we have destroyed all ships.
if the server is shooting at one of our ship positions and misses it is our turn to shoot.
Otherwise we asses the damage and feedback to the server after reflecting the damages in ourBoard -
the enemy wins if all our ships are destroyed
The Damage class has two constructors, one for reading enemy damage (first case) and another for
asseesing our own damage passing it the state of our naval forces and the location hit
*/
public void run()
{
try {
while(!positioned) Thread.sleep(500);
bombAtEnemy();
while (true)
{
int header = fromServer.readInt();
int pos;
if ( header == RESPONSE )
{
Damage enemyDamage = new Damage(fromServer);
if (enemyDamage.isShipHit())
{
enemyBoard.setHit(enemyDamage.getPositionHit());
if (enemyDamage.isShipDestroyed() )
{
enemyBoard.display(enemyDamage.getShipDestroyed());
if (enemyDamage.areAllShipsDestroyed() )
{
JOptionPane.showMessageDialog(null,"You have won","Game Over",
JOptionPane.WARNING_MESSAGE);
startButton.setText("Game Over. You won.");
break;
}
}
bombAtEnemy();
}
}
else if ( header == BOMBED_AT )
{
pos = fromServer.readInt();
Damage ourDamage = new Damage(nf,pos);
if (ourDamage.isShipHit() )
{
ourBoard.setHit(pos);
if ( ourDamage.isShipDestroyed())
ourBoard.display(ourDamage.getShipDestroyed(),Color.black);
ourDamage.send(toServer);
if ( ourDamage.areAllShipsDestroyed() )
{
JOptionPane.showMessageDialog(null,"Opponent has won","Game Over",
JOptionPane.WARNING_MESSAGE);
startButton.setText("Game Over - Opponent Won");
break;
}
}
else {
ourBoard.setCross(pos);
bombAtEnemy();
}
}
}
}
catch(Exception ex)
{
System.out.println(ex);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -