
Si hay algún juego favorito por profesores de universidad para enviar como prácticas, este es el buscaminas. Este juego fue creado por Robert Donner en 1989. Se hizo popular gracias a que se incluyó en el sistema operativo Windows 3.11. Desde entonces, ha permanecido en todas sus posteriores versiones.
A pesar de ser archiconocido este juego, es muy fácil desarrollar una versión aceptable o amateur. En este post vamos a explicar las funcionalidades más interesantes a tener en cuenta en el momento del desarrollo. Finalmente, dejaremos un código fuente en Java de ejemplo.
Reglas del juego
Este apartado lo explicaré resumido ya que en la actualidad es de conocimiento común las reglas de este juego. El juego se basa en un tablero de NxM casillas que pueden ser minas o terreno. El objetivo del juego es descubrir, haciendo clic sobre las casillas, las celdas que son terreno sin hacer clic a las minas.
Para ello, cuando se hace clic sobre terreno (sin mina) se visualiza un número con el total de minas que hay alrededor. Si se hace clic sobre terreno sin mina alrededor, se descubren las celdas continuas sucesivamente hasta que se encuentran terrenos con minas cerca. Si se hace clic sobre una mina se acaba la partida. Por último, si se tiene la sospecha de que existe una mina, se puede marcar la casilla pulsando el botón derecho.
En un buscaminas cualquiera es posible que el número de casillas y el número de minas pueda ser configurable por el usuario. En nuestro caso vamos a crear un tablero de 10×10 casillas con 10 minas en el tablero. Pintaremos de rojo las minas y de azul las celdas marcadas con una bandera.
Estructura del juego
La programación del juego se basa en dos algoritmos importantes de lógica y un algoritmo de visualización. Los algoritmos considerables a estudiar son: rellenar el tablero aleatoriamente y hacer que cuando se pulse una casilla sin mina cerca se despliegue el terreno hasta encontrar celdas cercanas a minas. En cuanto al algoritmo de visualización dependerá del modo de visualización que utilices: lienzo, botones, html, etc. Los dos algoritmos de lógica los veremos en próximos apartados.
Por otra parte, como elementos secundarios (pero esenciales), existirá una variable de estado que indicará si se está jugando (0), hemos perdido (1) o hemos ganado (2). Igualmente, cada celda tendrá también un estado que indicará si está tapada (0), está descubierta (1) o si se ha puesto una bandera (2).
También existe una variable casillasVistas que nos indicará cuantas casillas hemos descubierto para detectar si hemos conseguido el objetivo. Esta variable no es obligatoria ya que se puede calcular cada vez que destapamos una celda contando los estados de celdas descubiertas. Sin embargo, por motivos de eficiencia y evitar escribir más código, es bastante recomendable hacer esta variable.
Rellenar el tablero aleatorio
En muchas ocasiones al trasladar una imagen visual al problema tendemos a imitar lo que vemos. Por ello, lo más intuitivo es crear una matriz para almacenar el tablero. Sin embargo, esta no es la única solución ya que se podrían almacenar las minas en un vector dinámico y realizar las consultas únicamente recorriendo este vector de minas.
Para nuestro ejemplo … no vamos a ser tan malos y vamos a crear el tablero en forma de matriz para que sea más fácil de entender. En el momento de plantear un tablero en forma de matriz se pueden plantear dos posibles formas de trabajo. Poner únicamente las minas y calcular cuantas hay cerca al hacer clic o precalcular los números de las casillas con las minas cercanas al principio de la partida.
Para nuestro caso, programaremos la segunda opción y precalcularemos todo. Como consecuencia, como almacenaremos valores de 0 a 8 indicando cuantas minas hay alrededor, no podremos utilizarlos para representar una mina así que utilizaremos el valor 9 para indicar que hay una mina.
El primer problema que nos podemos encontrar es como poner 10 minas aleatoriamente. En el momento de poner una mina, al obtenerlo aleatoriamente hay que tener cuidado de no poner dos minas en la misma casilla. Por ello, deberemos repetir el resultado hasta que encuentre un sitio libre de mina. En todo caso es importante controlar que el número de minas es menor o igual al total de celdas ya que en caso contrario podría producirse un bucle infinito. Esta verificación puede realizarse en el momento de ejecutar el método o en el momento que el usuario cambia la configuración de minas, filas o columnas no permitiendo valores incoherentes al problema.
hacer{
obtener fila y columna al azar
}mientras que (tablero[fila][columna]==mina);
Con esto ya tendríamos el algoritmo que pone las minas, pero faltaría obtener los valores 1-8 de las celdas cercanas a las minas. Esto se puede hacer de dos maneras: calcularlo al final de poner todas las minas o incrementar el alrededor de la celda cuando se una mina. Nosotros utilizaremos esta segunda.
En el momento de recorrer el alrededor de una mina hay que tener cuidado de no salirse del tablero en el caso de poner la mina en un lateral o esquina. La solución intuitiva es utilizar un if en el recorrido comprobando que no se sale del vector. Sin embargo, se puede sacar una solución más corta conociendo las funciones mínimo y máximo.
para fila2 = max(0,fila-1) hasta min(TAM-1, fila+1) hacer
para columna2 = max(0,col-1) hasta min(TAM-1, col+1) hacer
si (no es mina tablero[fila2, columna2]) entonces
tablero[fila2,columna2]++
fin de si
fin de para
fin de para
Pulsar una celda sin minas alrededor
Otro reto que se encuentran los alumnos en este juego es descubrir más celdas en el caso de que no haya minas cerca. A simple vista, la solución intuitiva para un alumno novel es utilizar una iteración while, por no saber cuantas celdas (veces) se van a recorrer. Sin embargo, la siguiente duda que viene es como hacer que se recorra un área, bastante asimétrica, por cierto.
El while es una posible solución combinándola con un vector dinámico, sin embargo aprender a utilizar algoritmos recursivos para este caso es una buena idea. No hay que olvidar que una iteración while es una iteración lineal (de cada elemento solo puede ejecutar uno a continuación). Sin embargo, los algoritmos recursivos nos permiten que de cada elemento se ejecuten N elementos similares. En el caso del buscaminas de cada celda se pueden destapar 8 celdas más. El algoritmo de manera resumida sería algo así
si es celda a destapar entonces
destapa celda
si tiene valor 0 entonces
para todas las celdas de alrededor hacer
si no es mina entonces
hacer clic recursivamente en las celdas cercanas
fin de si
fin de para
fin de si
fin de si
Código fuente del buscaminas
Finalmente aquí tenéis un código fuente de ejemplo de buscaminas que podeis tomar como ejemplo y, por supuesto, probarlo y modificarlo.
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
public class Buscaminas extends JFrame {
public static int TAM=10;
private int tablero[][]=new int[TAM][TAM]; //Representación del tablero
private int visible[][]=new int[TAM][TAM]; //0 tapado, 1 descubierto, 2 bandera
private int estado=0; //0 jugando, 1 game over, 2 victoria
private int casillasVistas=0; //Contador de casillas vistas
public Buscaminas(){
//Configuracion de la ventana
setVisible(true);
setSize(405, 440);
setTitle( "Buscaminas casero. By Jorge Rubira" );
setResizable(false);
//Crea el tablero
crearTablero();
//Eventos al pulsar el raton
addMouseListener(new MouseListener() {
public void mouseReleased(MouseEvent arg) {
//Si estamos jugando
if (estado==0){
//Obtiene fila y columna pulsada
int f=(arg.getY()-40)/40;
int c=arg.getX()/40;
if (arg.getButton()==MouseEvent.BUTTON1){
if (visible[f][c]==0){
if (tablero[f][c]==9){
//Si pulsa una mina acaba la partida
gameOver();
}else{
//Si pulsa un terreno lo visualiza ejecutando una funcion recursiva
clicCasilla(f,c);
}
}
}else if (arg.getButton()==MouseEvent.BUTTON3){
if (visible[f][c]==0){
visible[f][c]=2;
}else if (visible[f][c]==2){
visible[f][c]=0;
}
}
}else{
crearTablero();
}
repaint();
}
public void mousePressed(MouseEvent arg0) {}
public void mouseClicked(MouseEvent e) {}
public void mouseExited(MouseEvent arg0) {}
public void mouseEntered(MouseEvent arg0) {}
});
addWindowListener(new WindowListener() {
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
public void windowOpened(WindowEvent arg0) {}
public void windowIconified(WindowEvent arg0) {}
public void windowDeiconified(WindowEvent arg0) {}
public void windowDeactivated(WindowEvent arg0) {}
public void windowClosed(WindowEvent arg0) {}
public void windowActivated(WindowEvent arg0) {}
});
}
public void gameOver(){
estado=1;
}
public void victoria(){
estado=2;
}
public void clicCasilla(int f, int c){
//Si la casilla esta tapada
if (visible[f][c]==0){
//Descubre la casilla
visible[f][c]=1;
casillasVistas++;
if (casillasVistas==90){
//Si llega a las 90 casillas descubiertas gana
victoria();
}else{
//Si no hay minas cercanas
if (tablero[f][c]==0){
//Recorre las casillas cercanas y tambien las ejecuta
for (int f2=max(0, f-1);f2 < min(TAM,f+2);f2++){
for (int c2=max(0,c-1);c2 < min(TAM,c+2);c2++){
clicCasilla(f2, c2);
}
}
}
}
}
}
public void crearTablero(){
//Inicializa el tablero
for (int f=0;f < TAM;f++){
for (int c=0;c < TAM;c++){
tablero[f][c]=0;
visible[f][c]=0;
}
}
estado=0;
casillasVistas=0;
//Pone diez minas
for (int mina=0;mina < 10;mina++){
//Busca una posición aleatoria donde no haya otra bomba
int f,c;
do{
f=(int)(Math.random()*10);
c=(int)(Math.random()*10);
}while(tablero[f][c]==9);
//Pone la bomba
tablero[f][c]=9;
//Recorre el contorno de la bomba e incrementa los contadores
for (int f2=max(0, f-1);f2 < min(TAM,f+2);f2++){
for (int c2=max(0,c-1);c2 < min(TAM,c+2);c2++){
if (tablero[f2][c2]!=9){ //Si no es bomba
tablero[f2][c2]++; //Incrementa el contador
}
}
}
}
}
public void update(Graphics g){
paint(g);
}
public void paint(Graphics g){
g.setFont(new Font( "ARIAL" , Font.BOLD, 14));
g.clearRect(0, 0, getWidth(), 40);
//Pinta las casillas
for (int f=0;f < TAM;f++){
for (int c=0;c < TAM;c++){
int x=c*40;
int y=f*40+40;
if (visible[f][c]==0 && estado==0){
g.setColor(Color.gray);
g.fillRect(x, y, 40, 40);
}else if (visible[f][c]==2 && estado==0){
g.setColor(Color.blue);
g.fillRect(x, y, 40, 40);
}else if (tablero[f][c] < 9){
g.setColor(Color.white);
g.fillRect(x, y, 40, 40);
if (tablero[f][c]>0){
g.setColor(Color.black);
g.drawString( ""+tablero[f][c], x+15, y+25);
}
}else{
g.setColor(Color.red);
g.fillRect(x, y, 40, 40);
}
g.setColor(Color.DARK_GRAY);
g.drawRect(x, y, 40, 40);
}
}
//Texto de estados (no jugando)
if (estado==1){
g.drawString( "Game over", 10, 40);
}
if (estado==2){
g.drawString( "Conseguido!!!", 10, 40);
}
}
//Funciones para abreviar letras.
public int max(int a, int b){
return Math.max(a,b);
}
public int min(int a, int b){
return Math.min(a,b);
}
public static void main(String arg[]){
new Buscaminas();
}
}
Comentarios
¡Ah, el buscaminas, que recuerdos! Yo me programé uno en la calculadora Texas Instruments 82. Lo que me podía llegar a aburrir en la biblioteca...
Resolver el buscaminas (o dar las posibles posiciones de minas conocidos X números) es un problema NP-completo.
Por si a alguien se le ocurría hacer un "solver"... XD XD XD
Interesante @Jose Juan, podríamos poner un concurso para ver quien hace un "solver" mucho más eficiente.
A mi me tocó programarlo con 'Caml' no hace mucho como práctica de una asignatura. Costó muchas horas dedicadas y muchos dolores de cabeza, pero lo bien que se siente uno después... xD
Yo lo tuve que hacer en ensamblador, la verdad es que a los profesores les gusta mucho jejeje
Hola,
Esto:
hacer{obtener fila y columna al azar
} mientras que (tablero[fila][columna]==mina);
que en java pones como:
do{f=(int)(Math.random()*10);c=(int)(Math.random()*10);
} while(tablero[f][c]==9);
es un error bastante gordo y que me he encontrado alguna vez que otra (el concepto), ¡puede crear un bucle infinito!
Cuidado con estas cosas que mucha gente lee esta página, entre otros ¡yo!
Saludos.
-- editado por última vez a las 00:08
Sólo si TAM <= 3 (pequeño tablero...), de todos modos, mucho mejor esta solución (para insertar todas de golpe):
// Si no hay minas previas (estamos inicializando)
Celdas.Sort( k => rnd.Next() ).Take(10).Each( k => k.mina = True );
// Si hay minas previas (añadimos minas)
Celdas.Where( k => !k.mina ).Sort( k => rnd.Next() ).Take(10).Each( k => k.mina = True );
-- editado por última vez a las 08:53
Buenas, Como tienes dos votos positivos ;) he puesto una mención a la importancia de controlar las variables con valores coherentes al problema. En mi opinión, esto es mejor controlarlo en el momento que el usuario modifique los parámetros FilasxColumnasxMinas, pero controlarlo en el propio bucle también es correcto. De hecho, cuando hay alguna duda en un while de que se vaya a colgar si están mal los datos, lo que suelo hacer es permitir que esa iteración do while no supere las (por ejemplo) 10000 iteraciones y si no sale ya que es mejor un fallo de aplicación que un cuelgue. Gracias por tus comentarios.
Hola,
Desde luego lo que expones también hay que tenerlo en cuenta, pero probablemente esa comprobación se haga en el formulario que configura el juego o la hará el programador si es "hardcoded".
Mi comentario iba más en la línea de la respuesta de José Juan.
El problema es que controlas el bucle con variables aleatorias, y no puedes asegurar que las condiciones de salida se cumplan en un tiempo determinado (¿puedes calcular la complejidad de ese bucle?).
No hay necesidad de que esto sea así como ha mostrado José Juan (aunque su argumentación de que si TAM <= 3 es válido, tampoco me parece correcta).
Para clarificarlo un poco más va un ejemplo de los problemas que se producen dejando al azar estas cosas que le pasó a un amigo:
Tenía que crear un juego tipo hundir la flota, y una de las opciones era poder jugar contra el ordenador (e incluso ordenador vs ordenador).
Como era un poco vaguete, toda la IA que implemento para jugar contra el ordenador, era que este, la CPU, aleatoriamente excogiese una casilla.
Hizo un bucle muy parecido a este.
El programa algunas veces se le colgaba (aparentemente).
Y es que claro, cuando ya casi todas las casillas estaban seleccionadas, la probabilidad de escoger aleatoriamente una cerrada era muy baja, así que nunca terminaba (bueno en un tiempo infinito la probabilidad sería muy alta, pero tampoco asegura nada).
No me enrollo más, espero que haya quedado claro:
!Si podemos ser deterministas seámoslo! (todo es más sencillo :-P )
Si TAM <= 3, como deben tomarse 10 celdas, se quedará colgado o sí, o sí.
Si TAM = 4, la probabilidad de NO encontrar casilla para la 10ª mina es de 7/16=0,4375. Para considerar que semejante bucle está colgado, digamos que debe ejecutarse 50 veces (y eso que le llevará unos pocos milisegundos como mucho), bien, pues la probabilidad de tal suceso es de 0,4375^50<10^-18, muy mala suerte debes tener...
Obviamente para TAM>4 pues todavía es menor la probabilidad.
En general, la solución correcta es la que he propuesto o cualquier otra que sea determinista.
De todos modos noalavida (vaya nombre...) hay un buen número de problemas que únicamente pueden resolverse (sin usar fuerza bruta, claro) ¡arriesgándose a un bucle infinito! (todos los np-completos en que no valga una aproximación; ej. LPI, SAT3, ...).
-- editado por última vez a las 22:43
Tienes razón, aunque de todas formas ese tipo de algoritmos suelen llevar un registro de los nodos visitados (aunque reitero que no todos). En este caso, el nodo visitado sería cada posición aleatoria que genera random.
Es una cuestión de eficiencia que no viene al caso, ya que se presupone que es más bien algo anecdótico/didáctico orientado a programadores noveles.
brillante
Me encantaria ver un genbetagamedev. Claro, con gente que tenga experiencia en el tema.
Sería un puntazo! Además de que seguro que hay mucha gente buscando algo así. No se le puede proponer algo así a weblogs?
Excelente aporte, de los que más me han gustado ;)
Discrepo.
Desde hace ya algún tiempo, el juego estrella que suelen mandar los profesores es el Sudoku. El buscaminas ya pasó de moda, hombre xD.
Buen artículo.
A mi este año me ha tocado hacer un Tetris en Java. No me quedó muy mal :-).
package Recursividad;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.border.LineBorder;
import java.awt.Font;
@SuppressWarnings("serial")
public class Buscaminas extends JFrame implements ActionListener, MouseListener {
private JButton[][] tablero;
private JButton botonReset, botonPausa;
private JLabel labelCont, labelTiempo;
private JTextField txtCont;
private Icon mina = new ImageIcon(Buscaminas.class.getResource("/img/mina.png"));
private Icon bandera = new ImageIcon(Buscaminas.class.getResource("/img/bandera.png"));
private Timer temp = new Timer(1000, this);
private JTextField txtTiempo;
private int temporizador = 0;
public Buscaminas(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(0, 0, 750, 600);
setLocationRelativeTo(null);
getContentPane().setLayout(null);
getContentPane().setBackground(new java.awt.Color(120,150,250));
setTitle("Buscaminas");
tablero = new JButton[10][10];
{
botonReset = new JButton();
getContentPane().add(botonReset);
botonReset.setText("Reiniciar");
botonReset.setBounds(578, 274, 113, 50);
botonReset.addActionListener(this);
}
{
botonPausa = new JButton();
botonPausa.setText("Pausa");
botonPausa.setBounds(578, 202, 113, 50);
getContentPane().add(botonPausa);
botonPausa.addActionListener(this);
}
{
labelCont = new JLabel();
labelCont.setHorizontalAlignment(SwingConstants.RIGHT);
getContentPane().add(labelCont);
labelCont.setText("Bombas restantes:");
labelCont.setBounds(563, 29, 128, 16);
}
{
txtCont = new JTextField();
txtCont.setFont(new Font("Tahoma", Font.BOLD, 13));
getContentPane().add(txtCont);
txtCont.setBounds(621, 53, 70, 27);
txtCont.setEditable(false);
txtCont.setHorizontalAlignment(SwingConstants.CENTER);
}
{
labelTiempo = new JLabel();
labelTiempo.setHorizontalAlignment(SwingConstants.RIGHT);
getContentPane().add(labelTiempo);
labelTiempo.setText("Tiempo:");
labelTiempo.setBounds(563, 110, 128, 16);
}
{
txtTiempo = new JTextField();
txtTiempo.setFont(new Font("Tahoma", Font.BOLD, 13));
txtTiempo.setBounds(621, 133, 70, 27);
getContentPane().add(txtTiempo);
txtTiempo.setEditable(false);
txtTiempo.setText("0");
txtTiempo.setHorizontalAlignment(SwingConstants.CENTER);
}
pintarTablero();
disponerBombas();
contarBombasPerimetro();
setVisible(true);
}
public void pintarTablero(){
for (int f = 0; f < tablero.length; f++) {
for (int c = 0; c< tablero.length; c++) {
tablero[f][c] = new JButton();
tablero[f][c].setBounds(30+50*c, 30+50*f, 50,50);
tablero[f][c].setText("0");
tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20));
tablero[f][c].setBackground(new Color(200,190,230));
tablero[f][c].setForeground(new Color(200,190,230));
tablero[f][c].setBorder(new LineBorder(new java.awt.Color(120,150,250)));
tablero[f][c].setHorizontalAlignment(SwingConstants.CENTER);
tablero[f][c].setHorizontalTextPosition(SwingConstants.CENTER);
tablero[f][c].setToolTipText("["+ f + ", " + c + "]");
txtCont.setText("10");
tablero[f][c].addActionListener(this);
tablero[f][c].addMouseListener(this);
getContentPane().add(tablero[f][c]);
}
}
}
public void borrarTablero(){
for (int f = 0; f < tablero.length; f++) {
for (int c = 0; c< tablero.length; c++) {
tablero[f][c].setText("0");
tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20));
txtCont.setText("10");
tablero[f][c].setEnabled(true);
tablero[f][c].setBackground(new Color(200,190,230));
tablero[f][c].setForeground(new Color(200,190,230));
tablero[f][c].setIcon(null);
}
}
disponerBombas();
contarBombasPerimetro();
}
public void disponerBombas(){
int cont = 0;
while(cont != 10){
int posAzarF = (int)(Math.random()*10);
int posAzarC = (int)(Math.random()*10);
if(!tablero[posAzarF][posAzarC].getText().equals("*")){
tablero[posAzarF][posAzarC].setText("*");
tablero[posAzarF][posAzarC].setIcon(null);
cont++;
}
}
}
public void contarBombasPerimetro(){
for (int f = 0; f < tablero.length; f++) {
for (int c = 0; c< tablero.length; c++) {
if(tablero[f][c].getText().equals("0")){
int numeroBoton = contarCoordenadas(f, c);
tablero[f][c].setText("" + numeroBoton);
tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20));
}
}
}
}
public int contarCoordenadas(int f, int c){
int contador = 0;
if(f-1 >= 0 && c-1 >=0 && tablero[f-1][c-1].getText().equals("*")){
contador++;
}
if(f-1 >= 0 && tablero[f-1][c].getText().equals("*")){
contador++;
}
if(f-1 >= 0 && c+1 <10 && tablero[f-1][c+1].getText().equals("*")){
contador++;
}
if(c-1 >=0 && tablero[f][c-1].getText().equals("*")){
contador++;
}
if(c+1 <10 && tablero[f][c+1].getText().equals("*")){
contador++;
}
if(f+1 < 10 && c-1 >=0 && tablero[f+1][c-1].getText().equals("*")){
contador++;
}
if(f+1 < 10 && tablero[f+1][c].getText().equals("*")){
contador++;
}
if(f+1 < 10 && c+1 <10 && tablero[f+1][c+1].getText().equals("*")){
contador++;
}
return contador;
}
public void ponerBandera(int f, int c){
tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,0));
tablero[f][c].setIcon(bandera);
txtCont.setText(""+ (Integer.parseInt(txtCont.getText()) -1));
}
public void quitarBandera(int f, int c){
tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20));
tablero[f][c].setIcon(null);
txtCont.setText(""+ (Integer.parseInt(txtCont.getText()) +1));
}
public void desactivarJuego(){
for (int f = 0; f < tablero.length; f++) {
for (int c = 0; c< tablero.length; c++) {
tablero[f][c].setEnabled(false);
if(tablero[f][c].getIcon() == bandera){
tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20));
tablero[f][c].setIcon(null);
}
if(tablero[f][c].getText().equals("*")){
tablero[f][c].setForeground(Color.BLACK);
tablero[f][c].setBackground(Color.RED);
tablero[f][c].setIcon(mina);
tablero[f][c].setEnabled(true);
temp.stop();
}
if(tablero[f][c].getText().equals("0")){
tablero[f][c].setText("");
}
}
}
//JOptionPane.showMessageDialog(null, "¡¡¡Has perdido criaturilla!!!");
//temp.stop();
}
public void recorrer(int f, int c){
if(f >= 0 && f < 10 && c >= 0 && c < 10 ){
if(tablero[f][c].getText().equals("0") && tablero[f][c].getIcon() != bandera){
tablero[f][c].setText("");
tablero[f][c].setBackground(Color.WHITE);
recorrer(f, c+1);
recorrer(f+1, c);
recorrer(f-1, c);
recorrer(f, c-1);
recorrer(f+1, c+1);
recorrer(f-1, c-1);
recorrer(f+1, c-1);
recorrer(f-1, c+1);
}else if (tablero[f][c].getText().equals("1") && tablero[f][c].getIcon() != bandera){
tablero[f][c].setForeground(Color.BLACK);
tablero[f][c].setBackground(Color.WHITE);
tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20));
}else if (tablero[f][c].getText().equals("2") && tablero[f][c].getIcon() != bandera){
tablero[f][c].setForeground(Color.BLACK);
tablero[f][c].setBackground(Color.GREEN);
tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20));
}else if (tablero[f][c].getText().equals("3") &&
-- editado por última vez a las 10:14
Sudoku o Minas, la cuestión es prácticar.
Dicen que C# es una copia de Java ¿seguro?, mi versión en C# me gusta más
XD XD
(continuación)
&& tablero[f][c].getIcon() != bandera){ tablero[f][c].setForeground(Color.WHITE); tablero[f][c].setBackground(Color.BLUE); tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20)); }else if (tablero[f][c].getText().equals("4") && tablero[f][c].getIcon() != bandera){ tablero[f][c].setForeground(Color.WHITE); tablero[f][c].setBackground(Color.RED); tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20)); }else if (tablero[f][c].getText().equals("5") && tablero[f][c].getIcon() != bandera){ tablero[f][c].setForeground(Color.BLACK); tablero[f][c].setBackground(Color.WHITE); tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20)); }else if (tablero[f][c].getText().equals("6") && tablero[f][c].getIcon() != bandera){ tablero[f][c].setForeground(Color.BLACK); tablero[f][c].setBackground(Color.GREEN); tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20)); }else if (tablero[f][c].getText().equals("7") && tablero[f][c].getIcon() != bandera){ tablero[f][c].setForeground(Color.WHITE); tablero[f][c].setBackground(Color.BLUE); tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20)); }else if (tablero[f][c].getText().equals("8") && tablero[f][c].getIcon() != bandera){ tablero[f][c].setBackground(Color.BLUE); tablero[f][c].setForeground(Color.RED); tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20)); } } } public void triunfo(){ int contador = 100; for (int f = 0; f < tablero.length; f++) { for (int c = 0; c < tablero.length; c++) { Color colorTablero = tablero[f][c].getBackground(); Color colorOriginal = new Color(200,190,230); if(colorTablero.toString().equals(colorOriginal.toString())){ contador--; } } } if(contador == 90){ for (int f = 0; f < tablero.length; f++) { for (int c = 0; c < tablero.length; c++) { if(tablero[f][c].getText().equals("*")){ tablero[f][c].setForeground(Color.BLACK); tablero[f][c].setBackground(Color.RED); tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20)); tablero[f][c].setIcon(mina); if(tablero[f][c].getIcon() == bandera){ tablero[f][c].setFont(new java.awt.Font("Segoe UI",1,20)); tablero[f][c].setIcon(null); } } } } JOptionPane.showMessageDialog(null, "Has Ganado!!!"); temp.stop(); } } public void actionPerformed(ActionEvent e) { if(e.getSource() == temp){ temporizador++; txtTiempo.setText(""+temporizador); } if(e.getSource() == botonReset){ borrarTablero(); temp.stop(); txtTiempo.setText("0"); temporizador = 0; } if(e.getSource() == botonPausa){ temp.stop(); } for (int f = 0; f < tablero.length; f++) { for (int c = 0; c< tablero.length; c++) { if(e.getSource() == tablero[f][c]){ if(tablero[f][c].getText().equals("*") && tablero[f][c].getIcon() != bandera){ tablero[f][c].setEnabled(false); tablero[f][c].setBackground(Color.RED); desactivarJuego(); }else{ recorrer(f, c); triunfo(); } temp.start(); } } } } @Override public void mouseClicked(MouseEvent mo) { if(mo.getButton()== 3){ temp.start(); for (int f = 0; f < tablero.length; f++) { for (int c = 0; c < tablero.length; c++) { if(mo.getSource() == tablero[f][c]){ Color colorTablero = tablero[f][c].getBackground(); Color colorOriginal = new Color(200,190,230); if (tablero[f][c].getIcon() == null && colorTablero.toString().equals(colorOriginal.toString())){ ponerBandera(f, c); }else{ quitarBandera(f, c); } } } } } } @Override public void mouseEntered(MouseEvent mo) { } @Override public void mouseExited(MouseEvent mo) { } @Override public void mousePressed(MouseEvent mo) { } @Override public void mouseReleased(MouseEvent mo) { } public static void main(String[] args) { new Buscaminas(); } }Iconos png http://www.megaupload.com/?d=89LXJBVPNo lo puedo poner bien ni con etiquetas, que alguien me ayude y lo pongo bien. Gracias!
-- editado por última vez a las 11:59
Pues a mi me toca programar el juego de la vida. http://es.wikipedia.org/wiki/Juego_de_la_vida y en C.
Ya teneis la pregunta para esta semana.
-- editado por última vez a las 12:35
Un amigo me lo ha comentado y es cierto, parece ser que el pseudocódigo que habéis puesto tiene algún que otro error.
El primero que he visto es en el "if" que hay en el bucle, las variables que usáis para verificar si hay mina o no, deberían ser fila2 y columna2, y creo que la condición máxima de los bucles también están mal.
Buenas, Tienes razón y había un error en el nombre de las variables y estaban el max y min alternados. Ya lo he corregido. Gracias.
Hola desarrolladores, estoy intentando entender el código, pero cunado llego a "Poner mina" me pierdo. do{ f=(int)(Math.random()*10); c=(int)(Math.random()*10); }while(tablero[f][c]==9);
//Pone la bomba tablero[f][c]=9; porque la condicón de "while" es "tablero[f][c]==9" y se ubica la bomba en "tablero[f][c]=9;" alguien podría ayudarme a entender el código de "recorrer el contorno"? muchas gracias.
Buenas, lo que significa es busca una casilla aleatoria, si hay una mina busca otra casilla aleatoria, si en esta nueva hay una mina busca otra casilla aleatoria, etc ... finalmente cuando encuentre una casilla que no hay mina pone una mina en esa casilla. Es decir busca casillas aleatorias mientras encuentres minas puestas anteriormente, cuando encuentra una casilla sin mina, sal de while y pon la mina aquí. Saludos, Jorge
excelete tutorial... me costo un poquito entenderlo jejeje... ojala sigan haciendo post como estos.. gracias.
Yo viví en carne propia ese bucle infinito para colocar las bombas. Hace mucho había hecho un Buscaminas en Visual Basic 6 con la misma metodología. Cuando la proporción de bombas es una pequeña parte del tablero, no hay problema porque sale del bucle bastante rápido (es una cuestión de probabilidades). Cuando le agregué el modo personalizado me dí cuenta del problema. La solución que utilicé fué como matar la mosca con el martillo: hice un arreglo con los números de 1 a la cantidad de casillas del tablero e iba eligiendo aleatoriamente valores de ese arreglo, si el valor elegido tenía un bomba, avanzaba por el arreglo hasta encontrar uno que no tenga, y allí la colocaba.
El método no es el mejor pero funciona. :-)
No, no funciona, porque la probabilidad de caer la bomba en cada casilla no es uniforme.
(Las casillas vacías precedidas por N posiciones inválidas tienen N+1 veces más de probabilidad que una casilla vacía precedida por una vacía).
Como resultado, ¡tus tableros tenderán a tener las bombas agrupadas!
Tenés razón... No lo había pensado (eso es algo que hago poco). XD
Escribir un comentario
Para hacer un comentario es necesario que te identifiques: ENTRA o conéctate con FacebookConnect