Minesweeper clon kritik?

Status
Nicht offen für weitere Antworten.

zd

Bekanntes Mitglied
Hi,
bin atm dabei einen kleinen Minesweeper clon zu schreiben.
Dies ist mein erstes swing projekt, und ich habe extreme probleme (bzw teilweise keine ahnung), wie ich spiellogik und GUI trennen könnte. ich poste hier mal den code, wäre nett wenn ihr eure meinung dazu schreiben könntet. kann man das in etwa so lassen, oder muss ich das ganz anders angehen?

sonstige momentane mängel im spiel:
- man kann beim ersten klick eine mine erwischen
- das programm macht nichts, wenn man gewonnen hat
- man kann die spielfeldgröße + minenanzahl nur im code ändern (main methode)
- einige menüpunkte habe noch keine funktion
- kein zeitCounter, highscore etc


klasse mineplacer, erzeugt ein Minenfeld (erst werden minen in einem boolean array erzeugt, dann die zugehörigen zahlen auf den feldern aussenrum in einem int array gespeichert.
Code:
import java.util.*;

class MinePlacer
{
	//~ Constructors------------------------------------------------------------
	
	public MinePlacer(int fieldXSize, int fieldYSize, int minesToPlace)
	{
		mineField = new boolean[fieldXSize][fieldYSize];
		numberField = new int[fieldXSize][fieldYSize];
		Random randomGen = new Random();
		
		// seed mines randomly
		while (minesToPlace > 0)
		{
			int randomNumber = randomGen.nextInt(fieldXSize*fieldYSize);
			if (mineField[(randomNumber%fieldXSize)][randomNumber/fieldXSize] == false)
			{
				mineField[(randomNumber%fieldXSize)][randomNumber/fieldXSize] = true;
				minesToPlace --;
			}			
		}
		
		// count mines around each field
		for (int x = 0; x < fieldXSize; x ++)
		{
			for (int y = 0; y < fieldYSize; y++)
			{			
				// no mine on field
				if (mineField[x][y] == false)
				{
					int mineCounter = 0;
					if(x > 0)
					{
						if (y < (fieldYSize-1))
						{
							// north-west
							if (mineField[x-1][y+1])
								mineCounter ++;
						}
						// west
						if (mineField[x-1][y])
							mineCounter ++;
						if (y > 0)
						{
							// south-west
							if (mineField[x-1][y-1])
								mineCounter ++;
						}
					}
					if (y < (fieldYSize-1))
					{
						// north
						if (mineField[x][y+1])
							mineCounter ++;
					}
					if (y > 0)
					{
						// south
						if (mineField[x][y-1])
							mineCounter ++;
					}
					if (x < (fieldXSize-1))
					{
						if (y < (fieldYSize-1))
						{
							// north-east
							if (mineField[x+1][y+1])
								mineCounter ++;
						}
						// east
						if (mineField[x+1][y])
							mineCounter ++;
						if (y > 0)
						{
							// south-east
							if (mineField[x+1][y-1])
								mineCounter ++;
						}
					}
					numberField[x][y] = mineCounter;				
				}
				// mine on field
				else
					numberField[x][y] = -1;   
			}
		}		
	}
		
	//~ Methods-----------------------------------------------------------------
	
	public int getMines(int x, int y)
	{
		return numberField[x][y];
	}
		
	//~ Fields------------------------------------------------------------------
	
	boolean[][] mineField;
	int[][] numberField;
}

die klasse Minesweeper ist die hauptklasse, hier sind mainmethode, gui und spiellogik
Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class Minesweeper implements MouseListener, ActionListener
{
    //~ Private GUI-Methods-----------------------------------------------------
    
    private JMenuBar createMenuBar() 
    {      
        //Create the menu bar.
        menuBar = new JMenuBar();

        //----Build the game menu------ 
        menuGame = new JMenu("Game");
        menuBar.add(menuGame);

        //Gamemenu Items
        gameNew = new JMenuItem("New Game",KeyEvent.VK_N); 

        gameNew.addActionListener(this); 
        menuGame.add(gameNew); 
        gameLevel = new JMenuItem("Set Level...",KeyEvent.VK_L);
        menuGame.add(gameLevel);
        gameLevel.addActionListener(this);
        gameScores = new JMenuItem("Highscores",KeyEvent.VK_H);
        menuGame.add(gameScores);
        gameScores.addActionListener(this);
        gameExit = new JMenuItem("Exit Program",KeyEvent.VK_E);
        menuGame.add(gameExit);
        gameExit.addActionListener(this);
        
        //----Buil the Help menu-----
        menuHelp = new JMenu("Help");
        menuBar.add(menuHelp);
        
        // Helpmenu Items
        helpRules = new JMenuItem("Rules...",KeyEvent.VK_R);
        menuHelp.add(helpRules);
        helpRules.addActionListener(this);
        helpAbout = new JMenuItem("About",KeyEvent.VK_A);
        menuHelp.add(helpAbout);
        helpAbout.addActionListener(this);
                    
        return menuBar;
    }

    private Container createContentPane() 
    {
    	// Create Icons
		flag = new ImageIcon("img/mineFlag.jpg");
		flagWrong = new ImageIcon("img/mineFlagWrong.jpg");
		quest = new ImageIcon("img/questFlag.jpg");
		nothing = new ImageIcon("img/null.jpg");
		
		// Create Top-area
		newGame = new JButton(new ImageIcon("img/newGame.jpg"));
		newGame.setPreferredSize(new Dimension(25,25));
		newGame.addMouseListener(this);
		counterDisplay = new JTextField(""+minen);
		topArea = new JPanel();
		topArea.setPreferredSize(new Dimension(fieldXSize*25,40));
		topArea.add(newGame);
		topArea.add(counterDisplay);
		
		// Create PlayButton-area
		buttonArea = new JPanel();
		buttonArea.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
		buttonArea.setPreferredSize(new Dimension(fieldXSize*25,fieldYSize*25));
		createButtonField();
		
		contentPane = new JPanel();
		contentPane.setOpaque(true);
		contentPane.add(topArea);
		contentPane.add(buttonArea);
		
		return contentPane;
    }
    
    /** called from within createContentPane(), actionPerformed() for gameNew,
      * mouseListener for newGame 
      */
    private void createButtonField()
    {
       	minen = numberOfMines;
    	counterDisplay.setText(""+minen);
    	lost = false;
    	mines = new MinePlacer(fieldXSize,fieldYSize,minen);
        	
    	// Array to remember PlayButtons
		buttonArray = new PlayButton[fieldXSize][fieldYSize];  
		buttonArea.removeAll();
		
		for (int y=0; y<fieldYSize; y++)
		{
			for (int x=0; x<fieldXSize; x++)
			{
				int buttonType = mines.getMines(x,y);
				PlayButton button1 = new PlayButton(x,y,buttonType);
				button1.addMouseListener(this);
				buttonArea.add(button1);
				
				buttonArray[x][y] = button1;	
			}
		}
    }
    
    /** Create the GUI and show it. 
      */
    private static void createAndShowGUI() 
    {
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        JFrame frame = new JFrame("Minesweeper");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        Minesweeper demo = new Minesweeper();
        frame.setJMenuBar(demo.createMenuBar());
        frame.setContentPane(demo.createContentPane());

        //Display the window.
        frame.setSize(25*fieldXSize +25, 25*fieldYSize +150);
        frame.setLocation(250,250);
        frame.setVisible(true);
    }
    

 	//~ Private Game-Methods----------------------------------------------------

    private void buttonPressed(PlayButton butt)
	{
		int temp = butt.getButtonType();
		// clicked on mine
		if (temp == -1)
			this.lost();
		else 
		{
			buttonNoMinePressed(butt);
		}
			
		butt.setOpened();	
	}
	
	private void buttonNoMinePressed(PlayButton butt)
	{
		int temp = butt.getButtonType();
		if (temp == 0)
		{
				butt.setOpened();
				butt.setIcon(nothing);
				this.openAroundNull(butt.getXPos(),butt.getYPos());
		}
		
		else
		{
			butt.setOpened();
			butt.setText(StaticStrings.coloredNumber(temp));
		}
		buttonArea.validate();	
	}
	
	private void openAroundNull(int x, int y)
	{
		PlayButton tempButton;
		
		if (y>0)
		{
			if (x>0)
			{
				// north-west
				tempButton = buttonArray[x-1][y-1];
				if (tempButton.getOpened() == false)
					buttonNoMinePressed(tempButton);
			}			
			// west
			tempButton = buttonArray[x][y-1];
			if (tempButton.getOpened() == false)
				buttonNoMinePressed(tempButton);					
			if (x<(fieldXSize-1))
			{
				// south-west
				tempButton = buttonArray[x+1][y-1];
				if (tempButton.getOpened() == false)
					buttonNoMinePressed(tempButton);
			}
		}	
		if (x>0)
		{
			// north
			tempButton = buttonArray[x-1][y];
			if (tempButton.getOpened() == false)
				buttonNoMinePressed(tempButton);
		}
		if (x<(fieldXSize-1))
		{
			// south
			tempButton = buttonArray[x+1][y];
			if (tempButton.getOpened() == false)
				buttonNoMinePressed(tempButton);
		}
		if (y<(fieldYSize-1))
		{
			if (x>0)
			{
				// north-east
				tempButton = buttonArray[x-1][y+1];
				if (tempButton.getOpened() == false)
					buttonNoMinePressed(tempButton);
			}					
			// east
			tempButton = buttonArray[x][y+1];
			buttonNoMinePressed(tempButton);			
			if (x<(fieldXSize-1))
			{
				// south-east
				tempButton = buttonArray[x+1][y+1];
				if (tempButton.getOpened() == false)
					buttonNoMinePressed(tempButton);
			}
		}	
		buttonArea.validate();
	}
	
	private void lost()
	{
		lost = true;
		for (int i=0; i<fieldXSize; i++)
		{
			for (int j=0; j<fieldYSize; j++)
			{
				PlayButton butt = buttonArray[i][j];
				
				// reveal all not marked mines
				if (butt.getMarked() != 1 && butt.getButtonType() == -1)
				{
					butt.setIcon(new ImageIcon(""));
					butt.setText("<html><font color=#ED3415>*</font></html>");
				}
				
				// show wrongly mine-marked buttons
				else if (butt.getMarked() == 1 && butt.getButtonType() != -1)
				{
					butt.setIcon(flagWrong);	
				}
			} 
		}
		buttonArea.validate();
	}
	
	//~ Eventlistener-----------------------------------------------------------
	
	public void actionPerformed(ActionEvent ae)
	{
		Object eventQuelle = ae.getSource();
		
		// gameMenu
		if (eventQuelle == gameNew)
		{
			createButtonField();
			contentPane.validate();		
		}
	}
		
	public void mouseClicked(MouseEvent me)
	{
		Object mouseQuelle = me.getSource();
		
		if ((mouseQuelle instanceof PlayButton) && !lost)
		{
			PlayButton butt = (PlayButton)me.getSource();
			
			// leftClick
			if ((me.getButton() == 1) && (butt.getMarked() == 0))
			{
				buttonPressed(butt);
				buttonArea.validate();
			}
			
			// rightClick to mark/unmark as mine
			if ((me.getButton() == 3) && (butt.getOpened() == false))
			{
					// button not yet marked
					if (butt.getMarked() == 0)
					{
						butt.setIcon(flag);
						butt.setMarked(1);
						minen--;
						counterDisplay.setText(""+minen);						
					}
					// button marked as mine
					else if (butt.getMarked() == 1)
					{
						butt.setIcon(quest);
						butt.setMarked(2);
						minen++;
						counterDisplay.setText(""+minen);
					}
					// button marked as quest
					else if (butt.getMarked() == 2)
					{
						butt.setIcon(new ImageIcon(""));
						butt.setMarked(0);
					}
					buttonArea.validate();
			}
		}
		
		// start new game
		else if (mouseQuelle == newGame || mouseQuelle == gameNew)
		{
			createButtonField();
			contentPane.validate();			
		}
	}
		
	public void mousePressed(MouseEvent e) {
    }
    public void mouseReleased(MouseEvent e) {
    }
    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
    }
    
    
    //~ Main-Method-------------------------------------------------------------
        
    public static void main(String[] args) 
    {
        minen = 15;
        numberOfMines = 15;
        fieldXSize = 12;
        fieldYSize = 10;
        lost = false;
        javax.swing.SwingUtilities.invokeLater(new Runnable() 
        {
            public void run() 
            {
                createAndShowGUI();
            }
        });
    }
       
    //~ Fields------------------------------------------------------------------
	
	static int fieldXSize;
	static int fieldYSize;
	static int numberOfMines;
	static int minen;
	static boolean lost;
	
	MinePlacer mines;
	
	JMenuBar menuBar;
    JMenu menuGame;     
    JMenu menuHelp;
	JMenuItem gameNew;
    JMenuItem gameLevel;
    JMenuItem gameScores;
    JMenuItem gameExit;
    JMenuItem helpRules;
    JMenuItem helpAbout;
	
	JPanel topArea;
	JPanel buttonArea;
	JPanel contentPane;
	JTextField counterDisplay;
	
	JButton newGame;
	
	ImageIcon flag;
	ImageIcon flagWrong;
	ImageIcon quest;
	ImageIcon nothing;
	
	PlayButton[][] buttonArray;
}

die klasse PlayButton ist eine ableitung von JButton, erweitert um koordinaten und um einen "buttonTyp" (mine, 0,1,2,3,...)
Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;

public class PlayButton extends JButton
{
	//~ Constructors------------------------------------------------------------
	
	public PlayButton(int x, int y, int type)
	{
		super("");
		xPos = x;
		yPos = y;
		buttonType = type;
		this.setPreferredSize(new Dimension(25,25));
		this.setMargin(new Insets(2,2,2,2));
		clicked = false;		
	}
			
	//~ Methods-----------------------------------------------------------------

	public int getXPos()
	{
		return xPos;
	}
	
	public int getYPos()
	{
		return yPos;
	}
	
	public int getButtonType()
	{
		return buttonType;
	}
	
	public boolean getOpened()
	{
		return clicked;
	}
	
	public void setOpened()
	{
		clicked = true;
	}
	
	public int getMarked()
	{
		return mineMarked;
	}
	
	public void setMarked(int m)
	{
		mineMarked = m;
	}
	
	
	//~ Fields------------------------------------------------------------------
	
	String mineNumbers;
	int xPos;
	int yPos;
	boolean clicked;
	int mineMarked;
	int buttonType;	
}

StaticStrings speichert momentan nur die Strings für die anzeige der nr auf einem feld, später werden wohl mehr dazuokommen (z.B. strings für helpfile etc)
Code:
public class StaticStrings
{	
	/** Colored Numbers for not mined buttons
	  */
	static String coloredNumber(int n)
	{
		switch (n)
		{
			case 1: return "<html><font color=#410BDB>1</font></html>";				
			case 2:	return "<html><font color=#19EB3B>2</font></html>";
			case 3:	return "<html><font color=#EB4019>3</font></html>";
			case 4:	return "<html><font color=#070E68>4</font></html>";
			case 5:	return "<html><font color=#070E68>5</font></html>";
			case 6:	return "<html><font color=#070E68>6</font></html>";
			case 7:	return "<html><font color=#E6DF25>7</font></html>";
			case 8:	return "<html><font color=#E23ED8>8</font></html>";
			default: return "";
		}
	}
}

danke für eure mühe
 
B

bygones

Gast
sorry, ich habe nicht den ganzen Code genau durchgeschaut, aber ich kann dir ganz allgemein ein paar Tips geben:

Das "perfekte" System wäre folgendes:

Du hast 3 Ebenen:
Model <----> Controll <----> View

Die 3 Ebenen sollten unabhängig voneinander laufen (nur über Schnittstellendefinitionen), d.h. wenn du eines Tages ne andere GUI haben willst tauschst du einfach den View part aus, und das ganze sollte immer noch gehen.

Im Grunde gehört in View alles was mit der GUI zu tun hat (also reines anzeigen), Controll ist die Schnittstelle zwischen Model und View, also alles was mit der Kommunikation der beiden zu tun hat. Und in Model komment die Grundlegenden Datenmodell rein (die keine Verbuindung zur Controll bzw. GUI haben).

Wie gesagt, dass ganze ist "perfekt" - also nicht immer realisierbar - man sollte sich aber dran halten !

Hoffe konnte ein bisschen (theoretisch) helfen !
 

biker126

Bekanntes Mitglied
ich hab erst kürzlich n minesweeper clon gebastet. hab mich aber weder an das model-controll-view gehalten noch sonst schönen code gemacht ^^.

aber wenn du fragen zur spiellogik oder sonst was hast kann ich dir meinen code gerne mal geben... ;).
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben