Exercício-Programa

Propaganda
Exercício-Programa
Instruções:
Este EP divide-se em 2 partes :
a.Um relatório
b.Uma apresentação presencial
Do relatório devem constar:
- diagrama de classes completo (incluindo métodos ‘private’);
- respostas às questões propostas
- listagem completa do programa.
A apresentação presencial do EP inclui a demonstração da execução do programa e a
arguição individual. Assim, todos os elementos do grupo devem estar presentes à
apresentação. A nota individual, portanto, poderá ser diferente, tendo em vista as
respostas à arguição oral.
A nota será atribuída da seguinte forma:
Item
Diagrama de classes completo
Respostas corretas às questões
Implementação correta da
Parte 1
Implementação correta da
Parte 2
Arguição oral
Tempo estimado (horas)
1
0.5
16
Valor máximo
2
1
4
8
2
0.2
2
Note que, mesmo tendo um peso menor, a implementação da Parte 2, que trata do jogo
via Internet/Intranet, é bastante interessante!! Não deixe de fazê-la!!
Data da apresentação presencial: 3 de julho, entre 13h00 e 17h00, no laboratório,
para todos os alunos.
Descrição Geral
Deseja-se implementar um jogo de batalha naval, que permita os seguintes modos:
1. Jogador Humano contra Computador
2. Jogador Humano contra Jogador Humano, via rede
Para maior facilidade de implementação, as regras do jogo serão relaxadas no seguinte
sentido:
 Não é necessário verificar se o tabuleiro está corretamente preenchido;
 Não é necessário informar que tipo de embarcação foi atingida.
O diagrama de classes em anexo mostra o projeto do jogo. Ele foi preparado utilizando o
programa “MagicDraw” que conta com uma versão “community” para download e uso
grátis.
O método jogarBatalhaNaval() da classe BatalhaNaval (anexa) controla a execução do
jogo. Basicamente, consiste em :
_playerA.montarTabuleiroDefesa();
_playerB.montarTabuleiroDefesa();
boolean fim = false;
while (! fim) {
fim = jogar(_playerA, _playerB);
if (fim) {
_playerA.exibirMensagemVencedor();
_playerB.exibirMensagemPerdedor();
} else {
fim = jogar(_playerB, _playerA);
if (fim) {
_playerB.exibirMensagemVencedor();
_playerA.exibirMensagemPerdedor();
}
}
} // while
_playerA.terminar();
_playerB.terminar();
Fig.1 : Laço principal do jogo
Note, pelo projeto, que há 3 tipos de jogadores, cada qual correspondendo a uma classe:
JogadorHumano, JogadorComputadorizado e JogadorRemoto. Para permitir escrever a
lógica do jogo sem ter de levar em conta cada tipo de jogador, foi criada a interface
Player. Dessa forma, pode-se caracterizar que tipos de comportamentos são exigidos de
um jogador, qualquer que seja o seu tipo.
A classe Posicao será utilizada em vários pontos do programa, contendo uma linha,
coluna, que pode representar a posição de um navio ou um tiro.
A classe InterfaceGrafica (em anexo) contém uma proposta de interface gráfica, bastante
simples, embora funcional. Sugiro que você a utilize como está, para não perder tempo
criando uma nova. Ela trabalha em conjunto com a classe JogadorHumano (como
mostra a seta de ‘dependência’ no diagrama de classes).
As classes Tabuleiro, TabuleiroDefesa e TabuleiroAtaque contém a marcação dos
navios e dos tiros dados por um jogador. São semelhantes àquelas com que você
trabalhou em sala de aula.
JogadorHumano e InterfaceGrafica
Para que você não perca tempo codificando uma interface gráfica, a classe
InterfaceGráfica está em anexo.
Os métodos que estão implementados para fazer interface com a classe JogadorHumano
são :






InterfaceGrafica(JogadorHumano player) : construtor, que recebe como parâmetro
a referência da instância de JogadorHumano associada;
void exibirMensagem(String msg) : exibe uma mensagem na barra de ‘status’
void habilitarJogada() : libera o tabuleiro de ataque para uma nova jogada
boolean atirou() : retorna true se já há um tiro pronto para processamento
Posicao obterTiro() : retorna o último tiro, para processamento.
boolean tabuleiroDefesaPronto() : retorna true se o tabuleiro de defesa já foi
preparado.
Para facilitar a implementação (embora resulte em uma certa ineficiência), a inter-relação
entre a interface gráfica e a classe JogadorHumano é síncrona. Em outras palavras,
foram implementados métodos que permitem verificar se uma determinada atividade já
foi realizada e, se não foi, espera-se por um intervalo de tempo e verifica-se novamente.
Assim, por exemplo, a implementação do método
JogadorHumano.montarTabuleiroDefesa() torna-se:
public void montarTabuleiroDefesa(){
while (! _interface.tabuleiroDefesaPronto() ) {
try { Thread.sleep(1000); }catch (Exception e) {}
} // while
_defesaPronta = true;
} // montarTabuleirDefesa
Fig.2 : Método montarTabuleiroDefesa
Já o método JogadorHumano.obterJogada() pode ser implementado como:
public Posicao obterJogada() {
_jogadaPronta = false;
_interface.habilitarJogada();
_interface.exibirMensagem("Sua vez!");
while ( ! _interface.atirou() ) {
try { Thread.sleep(1000); } catch (Exception e) {}
} // while
_ultimaJogada = _interface.obterTiro();
_interface.exibirMensagem("");
_jogadaPronta = true;
return _ultimaJogada;
} // obterJogada
Fig.3: Método obterJogada
Adicionalmente, foram implementados para suporte ao modo de jogo via rede os
seguintes métodos:
 void exibirTiro(Posicao tiro) : exibe, graficamente, um tiro no tabuleiro de ataque.
Parte 1: Modo Jogador Humano contra Computador
Sempre que se implementa um comportamento ‘inteligente’ em um computador, é
preciso estabelecer algum tipo de heurística. Para o EP, sugiro que você utilize as
seguintes heurísticas:
JogadorComputadorizado.montarTabuleiroDefesa() : utilize uma configuração prédefinida e fixa para os navios do Computador.
JogadorComputadorizado.obterJogada() : faça com que o computador dê tiros
aleatoriamente. O método Math.random() pode ser utilizado para tal (não esqueça de
importar a classe java.lang.Math).
As heurísticas são bem simples, mas lembre-se: o objetivo do EP é o treinamento em
Orientação a Objetos!
Os demais métodos são de implementação simples.
Parte 2: Modo Jogador Humano x Jogador Humano, via rede
Para a comunicação via rede, é preciso que um lado faça uma requisição e o outro, tenha
a capacidade de respondê-la. Mas uma requisição pode chegar a qualquer instante! Para
permitir que isso aconteça, é preciso que haja uma ‘thread’ separada, somente para
atender às requisições remotas. Esta é a função da classe ServidorJogadorLocal : recebe
requisições do jogador remoto (por exemplo: o tabuleiro de defesa já está pronto ?) e é
capaz de interfacear com o JogadorHumano local e responder a este tipo de requisição. A
classe ServidorJogadorLocal é instanciada pela classe JogadorRemoto em seu construtor,
para que esteja pronta para atender este tipo de requisição o mais cedo possível.
Para organizar o relacionamento entre as classes ServidorJogadorLocal e
JogadorHumano (local), foi projetada a interface JogadorHumanoRemoto, que também é
implementada pela classe JogadorHumano.
Para jogar neste modo, na linha de comando para a invocação do programa devem ser
fornecidos 3 parâmetros: o endereço de rede do jogador remoto, a porta para conexão e a
porta que deve ser utilizada para receber as requisições do jogador remoto. Assim, por
exemplo, para testar localmente o programa, você poderia utilizar :
java BatalhaNaval localhost 8080 8081
e, em outra janela:
java BatalhaNaval localhost 8081 8080
(perceba que as portas são trocadas!)
As requisições e as respostas podem ser ‘strings’, com algum caracter especial (por
exemplo, ‘#’) sendo utilizado como terminador.
Questões
1.A interface Player foi criada para que a classe BatalhaNaval pudesse ser escrita de
forma genérica, sem levar em conta as especificidades de cada tipo de jogador (Humano,
Computadorizado, Remoto). Você acha que esta foi uma boa decisão de projeto ? Teria
sido melhor utilizar uma hierarquia de classes ? Justifique.
2.Foi estabelecida uma hierarquia de classes entre Tabuleiro, TabuleiroDefesa e
TabuleiroAtaque. Você acha que esta foi uma boa decisão de projeto ? Teria sido melhor
não utilizar a super-classe Tabuleiro ? Justifique.
Anexos
Diagrama de Classes
Classe BatalhaNaval
Classe InterfaceGrafica
Classe Posicao
public class BatalhaNaval {
public static int CONTRA_COMPUTADOR = 1;
public static int CONTRA_JOGADOR_REMOTO = 2;
private int _modo = CONTRA_COMPUTADOR;
private Player _playerA = null;
private Player _playerB = null;
private String _ipComputadorRemoto = "localhost";
private int _portaComputadorRemoto = 8080;
private int _portaLocal = 8081;
public static void main(String[] args) {
BatalhaNaval jogo = new BatalhaNaval();
jogo.jogarBatalhaNaval(args);
} // main
public void jogarBatalhaNaval(String[] args) {
try {
_modo = determinarModo(args);
configurar();
_playerA.montarTabuleiroDefesa();
_playerB.montarTabuleiroDefesa();
boolean fim = false;
while (! fim) {
fim = jogar(_playerA, _playerB);
if (fim) {
_playerA.exibirMensagemVencedor();
_playerB.exibirMensagemPerdedor();
} else {
fim = jogar(_playerB, _playerA);
if (fim) {
_playerB.exibirMensagemVencedor();
_playerA.exibirMensagemPerdedor();
}
}
} // while
_playerA.terminar();
_playerB.terminar();
} catch(Exception e) {
System.out.println ("Erro:" + e.toString());
e.printStackTrace();
}
} //main
public int obterModo() {
return _modo;
} // obterModo
private boolean jogar(Player atacante, Player defensor) throws Exception {
Posicao tiro = atacante.obterJogada();
defensor.exibirJogada(tiro);
return defensor.verificarFim();
} // jogar
private int determinarModo(String[] args) {
if (args.length == 0) {
return CONTRA_COMPUTADOR;
} else {
_ipComputadorRemoto = args[0];
_portaComputadorRemoto = Integer.parseInt(args[1]);
if (args.length == 3) {
_portaLocal = Integer.parseInt(args[2]);
}
return CONTRA_JOGADOR_REMOTO;
}
} // determinarModo
private void configurar() throws Exception {
if (_modo == CONTRA_COMPUTADOR) {
_playerA = new JogadorHumano();
_playerA.setModo(BatalhaNaval.CONTRA_COMPUTADOR);
_playerB = new JogadorComputadorizado();
_playerB.setModo(BatalhaNaval.CONTRA_COMPUTADOR);
} else {
_playerA = new JogadorHumano();
_playerA.setModo(BatalhaNaval.CONTRA_JOGADOR_REMOTO);
_playerB = new JogadorRemoto(_ipComputadorRemoto, _portaComputadorRemoto,
_portaLocal, (JogadorHumanoRemoto)_playerA);
_playerB.setModo(BatalhaNaval.CONTRA_JOGADOR_REMOTO);
}
} // configurar
} //
BatalhaNaval
import java.util.StringTokenizer;
public class Posicao {
public int _linha = 0;
public int _coluna = 0;
public Posicao (int linha, int coluna) {
_linha = linha;
_coluna = coluna;
} // construtor
public Posicao (String posicao) {
String str = posicao.substring(2); // pula a letra A ou D inicial
StringTokenizer st = new StringTokenizer(str, ",");
_linha = Integer.parseInt(st.nextToken());
_coluna = Integer.parseInt(st.nextToken());
}
} // Tiro
import java.awt.*;
import java.awt.event.*;
class InterfaceGrafica extends java.awt.Frame implements ActionListener {
Button[][] _defense = new Button[20][20];
Button[][] _attack = new Button[20][20];
Button _defesaPronta;
boolean _tabuleiroDefesaPronto = false;
JogadorHumano _player = null;
Posicao _tiro = null;
Label _status = null;
InterfaceGrafica(JogadorHumano player) {
_player = player;
initComponents();
createBoard(_defense, 10, 50, "d ");
createBoard(_attack, 300, 50, "a ");
enableButtons(_attack, false);
setVisible(true);
}
void exibirMensagem(String msg) {
_status.setText(msg);
_status.repaint();
} // exibirMensagem
void exibirTiro(Posicao t) {
int linha = t._linha;
int coluna = t._coluna;
Color c = _defense[linha][coluna].getBackground();
if ( c == Color.GRAY )
c = Color.GREEN;
else
c = Color.CYAN;
_defense[linha][coluna].setBackground(c);
} // exibirTiro
boolean tabuleiroDefesaPronto() {
return _tabuleiroDefesaPronto;
} // tabuleiroDefesaPronto
private void defesaPronta() {
enableButtons(_defense, false);
_defesaPronta.setEnabled(false);
_tabuleiroDefesaPronto = true;
} // defesaPronta
void habilitarJogada() {
_tiro = null;
enableButtons(_attack, true);
} // enableButtons
boolean atirou() {
if (_tiro == null)
return false;
return true;
} // atirou
Posicao obterTiro() {
return _tiro;
} // obterTiro
private void botaoAtaque(String posicao) {
enableButtons(_attack, false);
_tiro = new Posicao(posicao);
marcar(_attack, _tiro._linha, _tiro._coluna);
} // botaoAtaque
private void botaoDefesa(String posicao) {
_tiro = new Posicao(posicao);
marcar(_defense, _tiro._linha, _tiro._coluna);
_player.marcarNavio( _tiro);
} // botaoDefesa
public void actionPerformed(ActionEvent evt) {
String comando = evt.getActionCommand();
if (comando.equals("DefesaPronta")) {
defesaPronta();
} else if ( comando.startsWith("a", 0)) {
botaoAtaque(comando);
} else {
botaoDefesa(comando);
}
} // actionPerformed
private void createBoard(Button[][] board, int x0, int y0, String comando) {
for (int i=0; i < 20; i++) {
for (int j=0; j<20; j++){
Button b = new Button();
board[i][j] = b;
b.setBounds(x0 + (10 * j), y0 + (10* i), 10, 10);
b.setActionCommand(comando + i + "," + j);
b.setBackground(Color.GRAY);
b.addActionListener(this);
add(b);
}
}
} // createBoard
private void enableButtons(Button[][] board, boolean enable) {
for (int i=0; i < 20; i++) {
for (int j=0; j < 20; j++) {
board[i][j].setEnabled(enable);
}
}
} // disableButtons
private void marcar(Button[][] board, int linha, int coluna) {
Color c = board[linha][coluna].getBackground();
if ( c == Color.GRAY )
c = Color.BLUE;
else
c = Color.GRAY;
board[linha][coluna].setBackground(c);
} // marcar
private void initComponents() {
setLayout(null);
setBounds(0, 0, 530, 350);
setTitle("Batalha Naval");
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
exitForm(evt);
}
});
_defesaPronta = new java.awt.Button();
_defesaPronta.setActionCommand("DefesaPronta");
_defesaPronta.setLabel("Defesa OK");
_defesaPronta.addActionListener(this);
_defesaPronta.setBounds(10, 276, 90, 24);
add(_defesaPronta);
Label l1 = new Label();
l1.setBounds(10, 30, 50, 20);
l1.setText("Defesa");
add(l1);
Label l2 = new Label();
l2.setBounds(300, 30, 50, 20);
l2.setText("Ataque");
add(l2);
_status = new Label();
_status.setBounds(300, 250, 100, 20);
add(_status);
} // initComponents
/** Exit the Application */
private void exitForm(java.awt.event.WindowEvent evt) {
System.exit(0);
}
} // InterfaceGrafica
Download