# Schiffe versenken



## skappler (27. Jul 2011)

Hallo Leute
Ich habe gestern und heute als kleine Programmierübung begonnen Schiffe versenken zu programmieren.

Ich bin nun soweit, dass man zu Beginn seine schiffe setzt (bis jetzt 10 Stück mit einer größe von nur einem Feld) danach wird abwechselnd "geschossen". Der PC zielt dabei einfach nach dem Zufallsprinzip.
Wenn alle Schiffe versenkt wurden wird ausgegeben, wer gewonnen hat.

Der Code ist leider sehr unschön und vom Stil her auch eher schlecht programmiert, was davon kommt, dass ich recht planlos an die Sache rangegangn bin und einfach rumprobiert hab bis es klappt.

Hier der Code:


```
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.*;

import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;

import java.awt.*;

public class Battleships extends javax.swing.JApplet{
	private JButton jButton1;
	private JLabel yposlabel;
	private JLabel xposlabel;
	private JLabel jLabel1;

	char[][] myship = new char[10][10];
	char[][] myshoot = new char[10][10];
	char[][] enship = new char[10][10];
	char[][] enshoot = new char[10][10];
	private JLabel jLabel2;

	int blockx;
	int blocky;
	int blockxshoot;
	
	int mousex; 
	int mousey;
	
	boolean set = false;
	boolean play = false;
	
	int shipcounter = 10;
	
	int shipsdestroyed = 0;
	int enshipsdestroyed = 0;
	
	/**
	* Auto-generated main method to display this 
	* JApplet inside a new JFrame.
	*/
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				JFrame frame = new JFrame();
				Battleships inst = new Battleships();
				frame.getContentPane().add(inst);
				((JComponent)frame.getContentPane()).setPreferredSize(inst.getSize());
				frame.pack();
				frame.setVisible(true);
			}
		});

	} //main
	
	public Battleships() {
		super();
		initGUI();
		
		for(int i=0;i<10;i++){
			for(int j=0;j<10;j++){
				myshoot[i][j]=' ';
				myship[i][j]=' ';
				enshoot[i][j]=' ';
				enship[i][j]=' ';
					}
		}//for
		
		
	}
	
	private void initGUI() {
		try {
			this.setSize(670, 430);
			getContentPane().setLayout(null);
			this.addMouseMotionListener(new MouseMotionAdapter() {
				public void mouseMoved(MouseEvent evt) {
					thisMouseMoved(evt);
				}
			});
			this.addMouseListener(new MouseAdapter() {
				public void mouseClicked(MouseEvent evt) {
					thisMouseClicked(evt);
				}
			});
			{
				jButton1 = new JButton();
				getContentPane().add(jButton1);
				jButton1.setText("New Game");
				jButton1.setBounds(12, 49, 102, 21);
				jButton1.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						jButton1ActionPerformed(evt);
					}
				});
			}
			{
				jLabel1 = new JLabel();
				getContentPane().add(jLabel1);
				jLabel1.setText("Battleships v0.1");
				jLabel1.setBounds(17, 18, 162, 14);
			}
			{
				xposlabel = new JLabel();
				getContentPane().add(xposlabel);
				xposlabel.setBounds(159, 18, 36, 10);
			}
			{
				yposlabel = new JLabel();
				getContentPane().add(yposlabel);
				yposlabel.setBounds(194, 18, 74, 10);
			}
			{
				jLabel2 = new JLabel();
				getContentPane().add(jLabel2);
				jLabel2.setBounds(263, 16, 89, 14);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	
	
	
	public void paint(Graphics g){
		super.paint(g);
		g.drawRect(10, 100, 320, 320); //linkes spielfeld
		g.drawRect(340, 100, 320, 320); //rechte spielfeld
		
		//linke linien horizontal
		for(int i=0; i<=10;i++){
			g.drawLine(10, 100 + i*32, 330, 100+i*32);
		}
		
		
		//linke linien vertikal
		for(int i=0; i<=10;i++){
			g.drawLine(10 + i*32, 100, 10+i*32, 420);
		}
		
		//rechte linien horizontal
		for(int i=0; i<=10;i++){
			g.drawLine(340, 100 + i*32, 660, 100+i*32);
		}
		
		//rechte linien vertikal
		for(int i=0; i<=10;i++){
			g.drawLine(340 + i*32, 100, 340+i*32, 420);
		}		
		
		
		for(int i=0;i<10;i++){
			for(int j=0;j<10;j++){
		String hilf = String.valueOf(myship[i][j]);
		g.drawString(hilf,i*32+22,j*32+122);
		String hilf2 = String.valueOf(myshoot[i][j]);
		g.drawString(hilf2, i*32+352,j*32+122);
			}
		}
				
	}//paint
	
	
	
	public void placeShip(int x, int y, char[][] feld){
			
		if(check(x,y,feld)){
			feld[x][y]='s';
			shipcounter--;
		}
		
		repaint();
	}
	

	public boolean check(int x,int y, char[][] feld){
		
		// innenfeld
				if(x>0 && y>0 && x<9 && y <9){
				if(feld[x-1][y-1]==' ' && feld[x-1][y]==' '&& feld[x-1][y+1]==' '
						&& feld[x][y-1]==' '&& feld[x][y]==' '&& feld[x][y+1]==' '
						&& feld[x+1][y-1]==' '&& feld[x+1][y]==' '&& feld[x+1][y+1]==' '){
				return true;
				}//feldabfrage
				}//if >0
				
				
				//ecken
				if(x==0 && y==0){
					if(feld[x][y]==' ' && feld[x+1][y]==' ' && feld[x][y+1]==' ' &&feld[x+1][y+1]==' ' ){
						return true;
					}
				}//linke obere ecke
				
				if(x==0 && y==9){
					if(feld[x][y]==' ' && feld[x][y-1]==' ' && feld[x+1][y-1]==' ' &&feld[x+1][y]==' ' ){
						return true;
					}
				}//linke untere ecke
				
				if(x==9 && y==0){
					if(feld[x][y]==' ' && feld[x-1][y]==' ' && feld[x-1][y+1]==' ' &&feld[x][y+1]==' ' ){
						return true;
					}
				}//rechte obere ecke
				
				if(x==9 && y==9){
					if(feld[x][y]==' ' && feld[x-1][y]==' ' && feld[x-1][y-1]==' ' &&feld[x][y-1]==' ' ){
						return true;
					}
				}//rechte untere ecke
				
				
				//kanten
				if(x==0 && y!=0){
					if(feld[x][y-1]==' ' && feld[x][y]==' ' && feld[x][y+1]==' ' &&
							feld[x+1][y-1]==' ' && feld[x+1][y]==' ' && feld[x+1][y+1]==' '){
						return true;
					}
				}//linke kante
				
				if(x==9 && y!=0){
					if(feld[x][y-1]==' ' && feld[x][y]==' ' && feld[x][y+1]==' ' &&
							feld[x-1][y-1]==' ' && feld[x-1][y]==' ' && feld[x-1][y+1]==' '){
						return true;
					}
				}//rechte kante
				
				
				if(y==0 && x!=0){
					if(feld[x-1][y]==' ' && feld[x][y]==' ' && feld[x+1][y]==' ' &&
							feld[x-1][y+1]==' ' && feld[x][y+1]==' ' && feld[x+1][y+1]==' '){
						return true;
					}
				}//obere kante
				
				if(y==9 && x!=0){
					if(feld[x-1][y]==' ' && feld[x][y]==' ' && feld[x+1][y]==' ' &&
							feld[x-1][y-1]==' ' && feld[x][y-1]==' ' && feld[x+1][y-1]==' '){
						return true;
					}
				}//untere kante
	
		return false;
	}
	
	
	
	private void thisMouseClicked(MouseEvent evt) {
		//System.out.println("this.mouseClicked, event="+evt);
		//TODO add your code for this.mouseClicked
	
		blockx = (evt.getX()-10)/32;
		blocky = (evt.getY()-100)/32;
		
		blockxshoot = (evt.getX()-340)/32;
		System.out.println(blockxshoot+" "+blocky);
		
		
		if (set && blockx<=9){
			if(shipcounter>0){	
				placeShip(blockx,blocky,myship);
			}//if
			else{
				set=false;
				play = true;
			}
		}//if mode = set
		
		if (play){
			
			if(enship[blockxshoot][blocky]=='s'){
				myshoot[blockxshoot][blocky]='x';
				enshipsdestroyed++;
			}
			else{
				myshoot[blockxshoot][blocky]='O';
			}
			if(enshipsdestroyed==10) {
				jLabel2.setText("You Win!");
				play=false;
			}
			
			int ranx = (int) (Math.random()*10);
			int rany = (int) (Math.random()*10);
		
			if(myship[ranx][rany]=='s'){
				enshoot[ranx][rany]='x';
				myship[ranx][rany]='x';
				shipsdestroyed++;
			}
			else{
				enshoot[ranx][rany]='O';
				myship[ranx][rany]='O';
			}
			
			if(shipsdestroyed==10) {
				jLabel2.setText("You Lose!");
				play=false;
			}
			
			repaint();
		}
		
		
	}

	
	
	
	
	private void thisMouseMoved(MouseEvent evt) {
		//System.out.println("this.mouseMoved, event="+evt);
		//TODO add your code for this.mouseMoved
		mousex=evt.getX();
		mousey=evt.getY();
		xposlabel.setText(""+mousex);
		yposlabel.setText(""+mousey);
	}
	
	
	
	private void jButton1ActionPerformed(ActionEvent evt) {
		System.out.println("jButton1.actionPerformed, event="+evt);
		//TODO add your code for jButton1.actionPerformed
		jButton1.setText("Start");
		for(int i=0;i<10;i++){
			for(int j=0;j<10;j++){
				myshoot[i][j]=' ';
				myship[i][j]=' ';
				enshoot[i][j]=' ';
				enship[i][j]=' ';
					}
		}//for
		
		int hilf=0;
		while(hilf<10){
			
			int ranx = (int) (Math.random()*10);
			int rany = (int) (Math.random()*10);
			
			if(check(ranx,rany,enship)){
			enship[ranx][rany]='s';
			hilf++;
			}
		}
		
		set = true;
		play = false;
		shipcounter = 10;
		
	
		repaint();
	}
	

}
```


Ich habe nun folgende Probleme:
1. Wie kann ich am einfachsten Schiffe verschiedener Größe implementieren, die horizontal oder vertikal platziert werden können?
2. Dass man bei einem Treffer nochmal dran ist
3. Wie kann ich den KI Spieler "intelligenter" gestalten? Also zB, dass er nicht wieder auf das selbe Feld schießt das er schonmal beschossen hat oder (wenn die Großen Schiffe funktionieren) dass er bei einem Treffer versucht die anderen Teile des Schiffes versucht zu treffen.

Ich hoffe ihr könnt mir (trotz meines "unschönen" Programms) helfen

Gruß skappler


----------



## Fu3L (27. Jul 2011)

Mir ist nicht ganz klar, wie ich gegen den Computer spiele? Ich klicke in mein Feld und etwas passiert.. ich klicke in das Nachbarfeld und nichts passiert... Und es tauchen an seltsamen Stellen Nullen und Xe auf^^

Außerdem scmeißt es mir ständig Exceptions:


```
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -4
	at BattleShips.thisMouseClicked(BattleShips.java:282)
	at BattleShips.access$1(BattleShips.java:259)
	at BattleShips$3.mouseClicked(BattleShips.java:91)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
-2 1
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -2
	at BattleShips.thisMouseClicked(BattleShips.java:282)
	at BattleShips.access$1(BattleShips.java:259)
	at BattleShips$3.mouseClicked(BattleShips.java:91)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
-1 7
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
	at BattleShips.thisMouseClicked(BattleShips.java:282)
	at BattleShips.access$1(BattleShips.java:259)
	at BattleShips$3.mouseClicked(BattleShips.java:91)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
-1 8
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
	at BattleShips.thisMouseClicked(BattleShips.java:282)
	at BattleShips.access$1(BattleShips.java:259)
	at BattleShips$3.mouseClicked(BattleShips.java:91)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
-1 9
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
	at BattleShips.thisMouseClicked(BattleShips.java:282)
	at BattleShips.access$1(BattleShips.java:259)
	at BattleShips$3.mouseClicked(BattleShips.java:91)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
	at BattleShips.thisMouseClicked(BattleShips.java:282)
	at BattleShips.access$1(BattleShips.java:259)
	at BattleShips$3.mouseClicked(BattleShips.java:91)
-1 9
	at java.awt.Component.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
0 8
```

Die KI schlauer zu machen, dürfte erst einmal das leichteste sein: Gib der KI eine eigene Klasse und gib ihr eine Möglichkeit ihre bisherigen Schüsse zu speichern (in einer List zB). Dann suchst du so lange eine neue zufällige Position, bis eine gefunden ist, die nicht in der List vorhanden ist. 
Mit langen Schiffen: Wenn die KI irgendwo einen Treffer hatte, dann musst du das eben gesondert speichern und dann einfach, wie beim Menschen, über Versuch und Irrtum in der Gegen drumrum schießen lassen, bis du weißt, ob das Schiff horizontal oder vertikal steht und dann der Linie folgen.

Zu der PLatzierung längerer Schiffe, muss ich mir bei deinem Code länger Gedanken machen^^


----------



## nillehammer (27. Jul 2011)

> Dass man bei einem Treffer nochmal dran ist


Grundsätzlich nicht den ganzen Code in die Event-Handler packen, sondern in Methoden auslagern, anhand deren Namen erkennt, was die *fachlich* machen sollen. In dem konkreten Fall wäre z.B. folgender Code sinnwoll

```
private final boolean didHit(final int xCoordinate, final int yCoordinate) {
 ... hier die Koordinaten mit den Schiffspositionen vergleichen...
}

private final void destroyShipPart(x,y) {
 ... hier die entsprechende Koordinate aus dem Schiff löschen...
}

// Und in Deiner Zugsteuerung dann etwa so
...
while (didHit(x,y) {

  this.destroy(x,y);
}
```



> Wie kann ich den KI Spieler "intelligenter" gestalten? Also zB, dass er nicht wieder auf das selbe Feld schießt das er schonmal beschossen hat


Lass ihn die Koordinaten streichen, die er schon beschossen hat.

```
/* Die Methode benutzen, um KI-Spieler zu initialisieren, z.B. im Konstruktor oder wo es halt passt.*/
private final Map<Integer,Set<Integer>> initUnbeschosseneFelder()
  final Map<Integer,Set<Integer>> unbeschosseneFelder = new HashMap<Integer,<Set<Integer>>(maximaleZeilenanzahl);
  for (int zeile=1, zeile<=MAX_ZEILEN; zeile++) {
    final Set<Integer> spalten = new HashSet<Integer>(MAX_SPALTEN);
    for (int spalte=1; spalte<=MAX_SPALTEN; spalte++( {
      spalten.add(spalte);
    } 
    unbeschosseneFelder.put(zeile, spalten);
  }
  return unbeschosseneFelder;
}

/*KI-Spieler ermittelt,die Koordinaten auf die er schießen will */
int zeile, spalte;
do {
  zeile = irgendwas zufälliges;
  spalte = irgendwas zufälliges;
  if (unbeschosseneFelder.keySet().contains(zeile) && 
    ungeschosseneFelder.get(zeile).remove(spalte)   {
    break;
  }  
} while(true)
```


----------



## skappler (27. Jul 2011)

Fu3L hat gesagt.:


> Mir ist nicht ganz klar, wie ich gegen den Computer spiele? Ich klicke in mein Feld und etwas passiert.. ich klicke in das Nachbarfeld und nichts passiert... Und es tauchen an seltsamen Stellen Nullen und Xe auf^^
> 
> Außerdem scmeißt es mir ständig Exceptions:



Das ganze läuft abwechselnd. Jedesmal wenn man selbst klickt macht man einen schuß. Ein O steht für Wasser und ein X für einen Treffer. Direkt nach jedem Schuß ist der Gegner dran. Im rechten Feld sieht man wo man hinschießt und wo man getroffen hat. Im linken setzt man seine eigenen Schiffe und sieht wo der Gegner hinschießt.

Die Exceptions kommen daher wenn man im "Play Modus" ist, also schon die Schiffe gesetzt hat und dann nochmal ins linke Feld klickt. Das erzeugt ne Array Index out of Bound Exception, da man nur im rechten Feld klicken soll. Das ändert aber nix an der Funktion ist also nur ein Schönheitsfehler.




> Grundsätzlich nicht den ganzen Code in die Event-Handler packen, sondern in Methoden auslagern, anhand deren Namen erkennt, was die fachlich machen sollen. In dem konkreten Fall wäre z.B. folgender Code sinnwoll



Ich wollte das auch auslagern. Ich hab mal was über "Gameloops gelesen" und wollte das dann so regeln, hat aber nicht geklappt, weil ich nicht wusste wie ich abfrage ob geklickt wurde bzw in der Loop etwas durch den Klick ausführe

EDIT: Zu der while Schleife: Sowas in der Art hab ich schon probiert, aber das Programm hängt sich immer auf wenn ich eine Schleife in dem EventHandler mache


----------



## Fu3L (27. Jul 2011)

Ok, jetzt verstehe ich es besser^^ Ich war irritiert, weil meinen ersten Klick machte ich versehentlich im linken Feld und wenn du dort in der Reihe ganz rechts außen klickst, erscheint ein Kreis (oder eben evtl. ein x) in der Spalte links außen vom rechten Feld^^


----------



## skappler (27. Jul 2011)

Ja die Anleitung kommt noch xD 
Ich hab mir halt noch wenig mühe in bezug auf Layout und Optik gegeben, weil ich erstmal das Ding an sich zum Laufen bringen will.


----------

