Mostrar Mensajes

Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.


Mensajes - Kabuto

Páginas: 1 ... 22 23 24 25 26 [27] 28 29 30 31 32 ... 50
521
Ahora que tenemos ya más o menos establecidas las clases de los personajes, podemos pensar en hacer el main del programa.
Por desgracia, me quedo sin tiempo, pero puedo adelantar algo.

Creo que es interesante que al iniciarse el programa, lo primero que se encuentre el usuario es la posibilidad de crear su personaje, decidir la clase, nombre y los niveles de los atributos.
Para los niveles de ataque, defensa y vida he establecido unos límites mínimos y máximos.
Tu ya decides si quieres modificarlos.

Una vez creado el personaje, automáticamente se crea el Caballero que manejará la CPU y se muestra en pantalla las características del este personaje.

A partir de aquí debería comenzar el combate.
Mostrar menú para que el usuario elija atacar, defender o habilidades especiales (distintas según si maneja un Caballero o un Mago).
También habrá que decidir cómo ha de actuar la CPU. Esta deberá elegir al azar entre atacar, defender o Relámpago.

Y bueno, no me da tiempo para más por hoy. :-\
Solo he podido hacer la idea de la creación del personaje:

Código: [Seleccionar]
import java.util.Scanner;

public class Combate {

private static Scanner teclado = new Scanner(System.in);
private static Personaje jugador; //Podrá ser un Mago o un Caballero
private static Caballero CPU; //La CPU siempre es un Caballero

public static void main(String[] args) {

crearPersonaje();

//TODO Combatir...
}


private static void crearPersonaje() {
System.out.println("\t\tCREAR PERSONAJE");
System.out.println("\t\t----- ---------");
System.out.println("Clases disponibles:");
System.out.println("[1] -> Caballero");
System.out.println("[2] -> Mago");

System.out.print("Elige clase: ");
int clase = Integer.parseInt(teclado.nextLine());

System.out.print("\nNombre: ");
String nombre = teclado.nextLine();

System.out.print("Nivel de Ataque(min = 1/max = 10): ");
int ataque = Integer.parseInt(teclado.nextLine());
//Forzamos que el nivel escogido se ciña a los límites
if (ataque < 1)
ataque = 1;
else if (ataque > 10)
ataque = 10;

System.out.print("Nivel de Defensa(min = 1/max = 10): ");
int defensa = Integer.parseInt(teclado.nextLine());
if (defensa < 1)
defensa = 1;
else if (defensa > 10)
defensa = 10;

System.out.print("Nivel de Vida(min = 100/max = 1000): ");
int vida = Integer.parseInt(teclado.nextLine());
if (vida < 100)
vida = 100;
else if (vida > 1000)
vida = 1000;

//Tenemos todos los atributos necesarios, creamos Personaje
if (clase == 1) //Ha elegido Caballero
jugador = new Caballero(nombre, ataque, defensa, vida);
else //Mago
jugador = new Mago(nombre, ataque, defensa, vida);

System.out.println("\nEste es tu personaje:");
System.out.println(jugador);
pausa();

//Ahora creamos el Caballero CPU, con doble de potencia que el Jugador
CPU = new Caballero("Thanos", (ataque*2), (defensa*2), (vida*2));

System.out.println("Y este será tu rival:");
System.out.println(CPU);
System.out.println("Nivel de Ataque: " + CPU.getAtaque()
+ " / Nivel de Defensa: " + CPU.getDefensa());

pausa();
}


private static void pausa() {
System.out.println("\n\t\tPULSA ENTER PARA SEGUIR\n");
teclado.nextLine();
}
}

522
Veo que das algunos detalles más sobre los ataques de los personajes:

Citar
El caballero tiene una función especial invocar relámpago (Aquí el caballero debe tener la opción de usar la habilidad, aunque que sea golpe critico me gusto y no se como dejarlo)

El mago tiene una función especial de curar sus heridas y lanzar hechizos (Aquí el mago debe tener la opción de curarse así mismo 50 puntos vida y el hechizo es igual que el relampago del caballero).

Parece que los "ataques especiales" si son opcionales y no algo que deba ocurrir al azar.

Bien, podemos hacer que TODOS los personajes tengan la probabilidad de conseguir un ataque crítico. Así que esto lo incluiremos en la superclase.

Luego a parte, cada clase hija tendrá sus ataques especiales opcionales durante el combate.

El Caballero tendrá el Ataque Relámpago, que para distinguirlo del "ataque crítico" que habíamos ideado antes, pues podemos hacer que en lugar de un número al azar, la potencia del golpe sea el nivel de ataque total del personaje, incrementado un 50%.
Así siempre será un ataque poderoso.
Los "ataques críticos" no siempre son poderosos porque lo que hace es duplicar el valor de ataque obtenido al azar..., y si resulta que al azar hemos obtenido un paupérrimo 1.. pues ese 1 multiplicado por 2 tampoco es que sea una maravilla je je..

El Mago tendría dos habilidades, curarse 50 puntos y además un hechizo equivalente al Ataque Relámpago.

Bien, estas habilidades resultan muy ventajosas y quizás podría ser interesante ponerles algún límite. Como poder usarla solo un número determinado de veces, o que tras usarla no pudiera volver a seleccionarla hasta que transcurran 5 turnos...

Pero bueno, para no complicar más las cosas, y como el personaje se va a tener que enfrentar a un enemigo que es el doble de poderoso..., pues dejaremos que pueda escoger siempre esas habilidades.


Bien, pues si estamos de acuerdo con esto, la superclase Personaje quedaría modificada de la siguiente forma para dotarla de la probabilidad de conseguir un ataque crítico.
Así, tanto Mago como Caballero contarán con esta posibilidad.

Código: [Seleccionar]
import java.util.Random;

public class Personaje {
protected String nombre;
protected int ataque;
protected int defensa;
protected int vida;

public Personaje() {
nombre = "Desconocido";
ataque = 0;
defensa = 0;
vida = 0;
}
public Personaje(String nombre, int ataque, int defensa, int vida){
this.nombre = nombre;
this.ataque = ataque;
this.defensa = defensa;
this.vida = vida;
}

public void setNombre (String nom){
this.nombre = nom;
}
public void setAtaque (String atac){
this.nombre = atac;
}
public void setDefensa (String defn){
this.nombre = defn;
}
public void setVida (String pv){
this.nombre = pv;
}

public String getNombre (){
return this.nombre;
}
public int getAtaque (){
return this.ataque;
}
public int getDefensa (){
return this.defensa;
}
public int getVida (){
return this.vida;
}

public int atacar() {
Random azar = new Random();
//Ataque mínimo: 1, Maximo: según nivel de ataque
int puntosAtaque = azar.nextInt(ataque) + 1;

//Damos un 10% de posibilidad de obtener un "Ataque Crítico"
int valor = azar.nextInt(100);

if (valor%10 == 0) { //Comprobamos si es multiplo de 10
System.out.println("¡" + nombre + " consigue un Ataque Crítico!");
//Un "Ataque Crítico" duplica el daño del ataque
return puntosAtaque * 2;
}
else
return puntosAtaque;
}

public int defender() {
Random azar = new Random();
//Defensa mínima: 1, Maxima: según nivel de defensa
int puntosDefensa = azar.nextInt(defensa) + 1;

return puntosDefensa;
}

}

La clase Caballero hereda esa posibilidad de conseguir un crítico. Ahora el "Ataque Relámpago" es un tipo de ataque opcional al ataque normal y garantiza el daño máximo según nivel de ataque, incrementado un 50%.

Código: [Seleccionar]
public class Caballero extends Personaje{

public Caballero() {
super();
}

public Caballero(String nombre, int ataque, int defensa, int vida) {
super(nombre, ataque, defensa, vida);
}

public int ataqueRelampago() {
/*
* Nivel de ataque máximo, incrementado un 50%
*/
return (int)(ataque * 1.5);
}

@Override
public String toString() {
return String.format("Caballero: %s / Nivel de Vida: %d", nombre, vida);
}

}

Le he sobreescrito el método toString() para conseguir de forma fácil la clase de Personaje, nombre y nivel de vida.
Esta info la querremos mostrar en pantalla durante el combate, así que de este modo ya tenemos un String construido con esos datos.

Bueno, yo he optado por solo mostrar esos datos. Se puede modificar para que se muestren también nivel de ataque y defensa.

Para el Mago haremos lo mismo.
Esta clase ahora tiene dos habilidades opcionales.

Un "hechizo de ataque", que es equivalente al "ataque relámpago" del caballero.

Un "hechizo de curación" para recuperar 50 puntos de vida.
Ojo a este hechizo porque no es tan fácil como simplemente sumar +50 a la vida del personaje.
Los personajes tienen un nivel de vida inicial que decide al construir el objeto Personaje, ¿Qué pasa si lanzo este hechizo cuándo tengo la vida completa?
Pues entonces no debería curar nada, porque aún no hemos perdido vida. Si dejamos que se sumen los 50 puntos de vida siempre que elijamos este hechizo, podríamos incrementar el nivel de vida hasta el infinito..

Solo debe sumar 50 puntos de vida cuando nos falten esos 50 puntos respecto al nivel de vida inicial, o más.

Y si nos falta vida, pero la diferencia es menos de 50 puntos, pues entonces deberíamos sumar solo lo justo para cubrir esa diferencia.

Para controlar esto, a Mago le añado un atributo extra, una constante, para saber cuál es la vida máxima que el mago puede conseguir.
Así, cuando el usuario elija el hechizo de curar 50 puntos, curará más o menos según la vida que hayamos perdido. Incluso puede que no cure nada porque ya estamos al máximo de vida (y el usuario habrá mal gastado este turno)

Luego además tiene la posibilidad extra de recuperar 1 punto de vida (si es que le falta vida) con una probabilidad del 20%.
El método que se encarga de esto, antes lo puse como private y lo incluí como parte de los métodos de atacar y defender que se heredaban de la superclase.
Pero como ahora se amplían las posibilidades, he decidido no incluirlo en ningún método.
Será un método público al que invocaremos desde el programa principal durante el combate.

Código: [Seleccionar]
import java.util.Random;

public class Mago extends Personaje{

private final int VIDA_MAX; //Los hechizos de curación no pueden rebasar este límite

public Mago() {
super();
VIDA_MAX = vida; //Vida inicial es el límite máximo
}

public Mago(String nombre, int ataque, int defensa, int vida) {
super(nombre, ataque, defensa, vida);
VIDA_MAX = vida; //Vida inicial es el límite máximo
}

public void recuperarVida() {
/*
* 20% posibilidad de recuperar 1 punto de vida
* en cada turno de juego.
* Pero solo sí no se tiene ya la vida al máximo
*/
if (vida < VIDA_MAX) {
Random azar = new Random();
int valor = azar.nextInt(100);
if (valor%5 == 0) {//Comprobamos si es múltiplo de 5
System.out.println("¡" + nombre + " recupera 1 punto de vida!");
vida++;
}
}
}

public void curar50puntos() {

if (vida == VIDA_MAX)//No se puede recuperar vida porque ya está en su límite
System.out.println("El hechizo de curación no tiene ningún efecto");
else {
/*
* Calculamos diferencia entre vica actual y vida maxima
* para saber si podemos recuperar 50 puntos o menos.
*/
int diferencia = VIDA_MAX - vida;
if (diferencia >= 50) {
vida += 50;
System.out.println("¡" + nombre + " recupera 50 puntos de vida!");
}
else {
vida += diferencia;
System.out.println("¡" + nombre + " recupera " + diferencia + " puntos de vida!");
}
}
}

public int hechizoAtaque() {
/*
* Nivel de ataque máximo, incrementado un 50%
*/
return (int)(ataque * 1.5);
}

@Override
public String toString() {
return String.format("Mago: %s / Nivel de Vida: %d", nombre, vida);
}

}

523
He combinado los dos temas en uno solo, para que la información no quede dispersa.

524
Hola.
Lo primero es recomendar escoger un nombre más adecuado para la super clase.

Puesto que vamos a crear "personajes", pues así es como llamaría yo a la superclase: Personaje

Lo segundo, como el enunciado es algo parco en detalles (suele ser habitual), debemos decidir y tener claras las mecánicas del juego.

¿Qué ha de pasar si el jugador decide defender y no atacar? El enunciado no dice nada.

¿Qué es eso del ataque relámpago? ¿Es el ataque normal o es un ataque especial?
Es decir, ¿el Caballero va a tener dos tipos de ataque, "normal" y Relámpago?
Y si es así, ¿el Relámpago se escoge? ¿O es algo que va a ocurrir al azar?
Si es al azar, ¿que porcentaje de posibilidades decidimos que ocurra?
¿Y en que consiste? ¿Doble de daño? ¿Triple de daño?...

Y el Mago puede recuperar un punto de vida, vale, pero... ¿Siempre? ¿Al azar?
Si es al azar, ¿que porcentaje de posibilidades decidimos que ocurra?


A ver, lo que yo haría, para no complicar en exceso.

Los Personajes atacan y defienden.
Así que la superclase Personaje tendrá un método para atacar() y otro para defender().
Luego las clases hijas puede que modifiquen a su manera estos métodos.

¿Y en que van a consistir estos métodos de atacar y defender?
Pues podemos hacer que generen un número al azar entre 1 y su nivel de ataque si ataca o nivel de defensa si defiende.

Así decidimos la intensidad de su ataque o defensa.
Vale ¿y que hacemos con este número al azar?

Atacar está claro que restará vida al oponente. Si obtenemos un 25, pues el oponente recibiría 25 puntos de daño.

Defender, en cambio, lo que puede hacer es restar intensidad al ataque que se va a recibir.

Si se recibe un ataque de 25, pero en nuestro turno hemos elegido defender y el método defensa() nos ha dado por ejemplo un 10.
Pues los daños recibidos se reducen a 15 (25 - 10 = 15)
Incluso si la defensa es mayor o igual al ataque, este habría quedado bloqueado por completo y no se recibiría daño alguno.

Aunque esta mecánica de restar vida, restar intensidad al ataque recibido... se programará después, en el programa principal.

Ahora mismo solo necesitamos completar la clase Personaje con esos dos métodos: atacar() y defender().
Y que retornen un valor int al azar, entre 1 y su nivel de ataque/defensa

Código: [Seleccionar]
import java.util.Random;

public class Personaje {
protected String nombre;
protected int ataque;
protected int defensa;
protected int vida;

public Personaje() {
nombre = "Desconocido";
ataque = 0;
defensa = 0;
vida = 0;
}
public Personaje(String nombre, int ataque, int defensa, int vida){
this.nombre = nombre;
this.ataque = ataque;
this.defensa = defensa;
this.vida = vida;
}

public void setNombre (String nom){
this.nombre = nom;
}
public void setAtaque (String atac){
this.nombre = atac;
}
public void setDefensa (String defn){
this.nombre = defn;
}
public void setVida (String pv){
this.nombre = pv;
}

public String getNombre (){
return this.nombre;
}
public int getAtaque (){
return this.ataque;
}
public int getDefensa (){
return this.defensa;
}
public int getVida (){
return this.vida;
}

public int atacar() {
Random azar = new Random();
//Ataque mínimo: 1, Maximo: según nivel de ataque
int puntosAtaque = azar.nextInt(ataque) + 1;

return puntosAtaque;
}

public int defender() {
Random azar = new Random();
//Defensa mínima: 1, Maxima: según nivel de defensa
int puntosDefensa = azar.nextInt(defensa) + 1;

return puntosDefensa;
}

}

Bueno, a ver ahora las clases hijas.

Caballero, ¿qué hacemos con lo del Relámpago?

Yo lo consideraría un ataque especial que puede ocurrir al azar, una especie de "ataque crítico".
Podemos darle un 10% de posibilidades de que ocurra y que su efecto sea que duplique los puntos de ataque.
Ya que las posibilidades son bajas, al menos que su efecto sea potente.

Para esto, sobreescribimos el método atacar() que hereda de su superclase Personaje

Código: [Seleccionar]
import java.util.Random;

public class Caballero extends Personaje{

public Caballero() {
super();
}

public Caballero(String nombre, int ataque, int defensa, int vida) {
super(nombre, ataque, defensa, vida);
}

@Override
public int atacar() {
/*
* Caballero puede conseguir duplicar el daño de ataque
* con su habilidad "Ataque Relampago".
*/
int puntosAtaque = super.atacar();

//Damos un 10% de posibilidad de obtener un "Ataque Relampago"
Random azar = new Random();
int valor = azar.nextInt(100);

if (valor%10 == 0) { //Comprobamos si es multiplo de 10
System.out.println("¡" + nombre + " consigue un Ataque Relampago!");
//Un "Ataque Relampago" duplica el daño del ataque
return puntosAtaque * 2;
}
else
return puntosAtaque;
}

}


Sobre el Mago, puede recuperar 1 punto de vida en cada turno.
Podemos hacer que esto tenga un 20% de posibilidades de que ocurra.

Y decimos que puede ocurrir en cada turno, tanto si elige atacar como si elige defender.

Así que podemos escribirle a Mago un método privado que se encargue de decidir si recupera el punto de vida o no.
Y luego sobreescribimos los métodos atacar() y defender() de su superclase para que antes de que retorne el valor int generado al azar, se decida si recupera punto de vida o no.

Código: [Seleccionar]
import java.util.Random;

public class Mago extends Personaje{

public Mago() {
super();
}

public Mago(String nombre, int ataque, int defensa, int vida) {
super(nombre, ataque, defensa, vida);
}

private void recuperarVida() {
/*
* 20% posibilidad de recuperar 1 punto de vida
* en cada turno de juego
*/
Random azar = new Random();
int valor = azar.nextInt(100);
if (valor%5 == 0) {//Comprobamos si es múltiplo de 5
System.out.println("¡" + nombre + " recupera 1 punto de vida!");
vida++;
}
}

@Override
public int atacar() {
//Posibilidad de recuperar vida en turno de aque
recuperarVida();
return super.atacar();
}

@Override
public int defender() {
//Posibilidad de recuperar vida en turno de defensa
recuperarVida();
return super.defender();
}

}


Y con esto tendríamos las clases para los personajes.

Ahora habría que escribir el programa principal para crear personaje y combatir contra la máquina.
Antes de meternos en eso (además, tengo que irme  :P , se me acabó el tiempo...) repasa bien estas clases que he escrito.
Comprueba que entiendes estos códigos y pregunta si algo no te queda claro.
Decide si te parece bien lo que propongo, o si tienes un enunciado más completo con más datos de los que has publicado, indica si hay algo que no hacemos bien, o no hemos tenido en cuenta, o lo que sea...

Un saludo.

525
A ver, algunas explicaciones.

La clase Pokemon es básicamente la misma, con unos ligeros cambios.
He añadido dos atributos un String llamado mensaje y un boolean llamado suTurno

Antes cuando un Pokemon atacaba, mostraba en consola el daño producido y si era un ataque crítico.
Al pasar a interfaz gráfica ya no usamos la consola, pero esos mensajes nos siguen interesando obtenerlos para mostrarlos en la interfaz.
Así que ahora en lugar de imprimirlos en consola, tras cada ataque se compone un String nuevo que queda guardado en ese atributo.
Así tras la acción de atacar, podemos recuperar ese mensaje y enviarlo al panel superior de la interfaz que funciona como un "display" de mensajes.

El otro atributo, el boolean suTurno, es para que cada objeto Pokemon sepa decirnos si es su turno de ataque o no.
Antes, en modo consola, el turno estaba establecido directamente en el flujo de instrucciones del programa. Cada vez que se repetía el bucle while, atacaba el primer Pokemon y luego el segundo, si es que seguía vivo.
Ahora con la interfaz gráfica, no hay ningún bucle ni flujo de instrucciones que ejecuten los ataques.

Los ataques los produce el usuario pulsando los botones de cada Pokemon. Para garantizar que haya turnos, solo uno de los dos botones está activado. Cuando se pulsa un botón para atacar, este se desactiva y se activa el correspondiente al otro Pokemon para que haya "cambio de turno".

Para que la interfaz sepa en todo momento que botón ha de activar y cuál desactivar, hay que buscar una forma de hacérselo saber. Y se me ocurrió hacer que cada Pokemon tenga un boolean que cambie de true a false cuando ataque y a la inversa cuando reciba daño.
Así cada Pokemon puede decirle a su botón de ataque asociado si ha de estar activo o no.

Y eso es lo único que ha cambiado en la clase Pokemon:

Código: [Seleccionar]
public class Pokemon {

private String nombre;
private int vida;
private Random azar = new Random();

public String mensaje; //Mensaje que se genera tras un ataque
public boolean suTurno; //Indica si es el turno de ataque de este Pokemon

public Pokemon(String nombrePok, int vidaPok, boolean turno) {
nombre = nombrePok;
vida = vidaPok;
mensaje = "";
suTurno = turno;
}

public String getNombre() {
return nombre;
}

public int getVida() {
return vida;
}

public void setVida(int v) {
vida = v;
}

public int atacar() {

mensaje = ""; //Se generará un nuevo mensaje
int ataque = azar.nextInt(11) + 10;

if (esCritico()) {
mensaje += "¡Ataque crítico! ";
ataque =  (int)(ataque * 1.5);
}

mensaje += "Daño: " + ataque + " puntos";
return ataque;
}

public void perderVida(int ataque) {
vida -= ataque;
}

private boolean esCritico() {
int valor = azar.nextInt(100);
/*
* Entre 0 y 100 hay diez valores que son
* múltiplos de 10.
* Es decir, hay un 10% de posibilidades
* de obtener un valor múltiplo de 10.
*/

return valor%10 == 0;
}

@Override
public String toString() {
return "Nombre: " + nombre + "\nVida: " + vida;
}

}

Sobre la interfaz.
Es un JFrame compuesto básicamente de 4 paneles.
Cada panel proviene de una clase que hereda de JPanel (por eso es importante manejar bien POO para sacar partido a Swing)


En la parte superior tenemos un panel de la clase PanelMensajes que simplemente contiene un JLabel (etiqueta) para ir mostrando los mensajes que cada Pokemon genera al atacar.
Se adorna con algunos colores y mayor tamaño de fuente para que los mensajes destaquen más.
La clase tiene un método para recibir y mostrar dichos mensajes.

Código: [Seleccionar]
public class PanelMensajes extends JPanel{

private JLabel mensajes;

public PanelMensajes() {
JPanel display = new JPanel();
mensajes = new JLabel("¡Comienza la batalla!");
mensajes.setFont(new Font("Calibri", Font.PLAIN, 38));
mensajes.setForeground(new Color(243, 81, 55 ));
display.add(mensajes);

setLayout(new GridLayout(1, 1));
add(display);
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15),
BorderFactory.createEtchedBorder(EtchedBorder.RAISED)));
display.setBackground(new Color(230, 234, 160 ));
}

/*
* Recibe y muestra los mensajes que los Pokemon generan
* en cada ataque.
*/
public void setMensaje(String mensaje) {
mensajes.setText(mensaje);
}

}

Debajo de este panel superior, tenemos la parte central del JFrame, que es un panel compuesto de tres subpaneles.

Justo en el centro esta un panel de la clase PanelVS, que es tan simple que en realidad no valía la pena escribir una clase separada. Se podría haber compuesto directamente en la clase principal.
Pero bueno, nunca está de más modular el código.
Este panel simplemente muestra una imagen png que descargue de Internet, con las letras vs (versus).
Es tan simple como esto:
Código: [Seleccionar]
public class PanelVS extends JPanel{

public PanelVS() {
ImageIcon imagen = new ImageIcon(PanelVS.class.getClassLoader().getResource("img/vs.png"));
JLabel icono = new JLabel(imagen);
add(icono);
}

}

Como decía, este es el panel que está en medio de los tres paneles centrales.

Los otros dos paneles, uno a cada lado, provienen de la clase PanelPokemon, que ya sí es una clase más elaborada.
Este panel está a su vez compuesto de otros cuatro paneles dispuestos verticalmente.

El primer panel muestra el nombre del Pokemon al que está asociado.

El segundo panel muestra la foto del Pokemon.

El tercer panel contiene la barra de vida, que es un JProgressBar que se va reduciendo según el Pokemon asociado va perdiendo puntos de vida.

El cuarto y último panel contiene el botón para realizar ataques. Este botón es el que antes dijimos que se activa/desactiva según el Pokemon asociado informa de si es su turno de ataque o no.

Para que este botón funcione, hay que añadirle un ActionListener, una clase con el código que decide que ha de ocurrir cuando se pulse el botón.

Este ActionListener va a provenir de la clase principal y se lo haremos llegar mediante el método agregarAccion()

Código: [Seleccionar]
public class PanelPokemon extends JPanel{

private Pokemon pokemon; //Referencia al objeto Pokemon asociado a este panel
private JButton btAtacar;
private JProgressBar barraVida;

public PanelPokemon(Pokemon pok, String rutaImagen) {

pokemon = pok;

setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new PanelNombre());
add(new PanelImagen(rutaImagen));
add(new PanelVida());
add(new PanelBoton());

}

private class PanelNombre extends JPanel {

public PanelNombre() {
JLabel nombre = new JLabel(pokemon.getNombre());
nombre.setFont(new Font("Consolas", Font.BOLD, 34));
nombre.setForeground(Color.WHITE);
add(nombre);
setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.WHITE, Color.BLACK));
setBackground(new Color(23, 151, 238));
}
}

private class PanelImagen extends JPanel {

public PanelImagen(String rutaImagen) {
ImageIcon imagen = new ImageIcon(PanelPokemon.class.getClassLoader().getResource(rutaImagen));
JLabel icono = new JLabel(imagen);
icono.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(icono);
}
}

private class PanelVida extends JPanel {

public PanelVida() {

barraVida = new JProgressBar();
barraVida.setMaximum(pokemon.getVida());
barraVida.setValue(pokemon.getVida());
barraVida.setPreferredSize(new Dimension(200, 30));
barraVida.setForeground(Color.BLUE);
add(barraVida);
barraVida.setBorder(BorderFactory.createRaisedSoftBevelBorder());

TitledBorder bordeTitulado = new TitledBorder("Nivel de Vida");
bordeTitulado.setTitleFont(new Font("Consolas", Font.ITALIC, 24));
setBorder(bordeTitulado);
}
}

private class PanelBoton extends JPanel {

public PanelBoton() {
btAtacar = new JButton("¡ATACAR!");
btAtacar.setFont(new Font("Consolas", Font.BOLD, 30));
add(btAtacar);
setBorder(BorderFactory.createEmptyBorder(30, 20, 30, 30));
}
}

public void actualizarPanel() {
barraVida.setValue(pokemon.getVida());
btAtacar.setEnabled(pokemon.suTurno);
}

public void agregarAccion(ActionListener accion) {
btAtacar.addActionListener(accion);
}

}


¿Y por qué esa clase ActionListener la vamos a escribir en la clase principal?
Si el botón "ATACAR" está dentro de la clase PanelPokemon, ¿por qué no escribimos ahí su correspondiente código de acción?

Por un problema de "ámbitos". Una de las cosas que quiero que ocurran cuando se ataquen los Pokemon, es que el panel de mensajes reciba y muestre los mensajes que generan los Pokemon.
Pero este panel de mensajes, es una clase llamada PanelMensajes que está escrita separada de la clase PanelPokemon. Esto implica que están en ámbitos distintos, y desde PanelPokemon, no voy a tener visibilidad para actuar sobre PanelMensajes

En cambio, la clase principal, la que va a modelar el JFrame, va a contener dentro de su ámbito a TODAS las otras clases paneles.
Desde ahí si que no voy a tener problemas de visibilidad para hacer que unos paneles interactúen con otros.
Así que puedo escribir ahí la clase ActionListener y luego hacérsela llegar a sendos botones de "ATACAR".


La clase principal, llamada BatallaSwing tiene como atributos los dos Pokemon y referencias a los paneles que componen la interfaz.
Excepto para la clase PanelVS, que como está de puro adorno y no se interactúa con ella durante el programa, no necesita estar referenciada con ningún atributo.

En esta clase podemos ver el código del ActionListener, que viene a ser el "motor" del juego.
Cuando se pulsa un botón de ataque, se pregunta a los Pokemon de quién es el turno (se podría preguntar qué botón ha sido pulsado, pero es más rápido consultar el atributo de los Pokemon).
Entonces el Pokemon que posee el turno ataca al otro.
La interfaz se actualiza con mensaje arriba y las barras de vida.
Y se comprueba si ambos Pokemon siguen con vida.
Si alguno ha muerto, se informa del final del juego con un JOptionPane, que al aceptarlo, se reinicia todo a valores por defecto y el juego comienza de nuevo.

Código: [Seleccionar]
public class BatallaSwing extends JFrame{

private Pokemon pok1;
private Pokemon pok2;
private PanelMensajes pnMensajes;
private PanelPokemon pnPok1;
private PanelPokemon pnPok2;

public BatallaSwing() {

pok1 = new Pokemon("Pikachu", 100, true);
pok2 = new Pokemon("Bulbasaur", 100, false);
pnMensajes = new PanelMensajes();
pnPok1 = new PanelPokemon(pok1, "img/pikachu.png");
pnPok1.actualizarPanel();
pnPok1.agregarAccion(new AccionAtacar());
pnPok2 = new PanelPokemon(pok2, "img/bulbasaur.png");
pnPok2.actualizarPanel();
pnPok2.agregarAccion(new AccionAtacar());

setLayout(new BorderLayout());

add(pnMensajes, BorderLayout.NORTH);
JPanel pnCentro = new JPanel();
pnCentro.add(pnPok1);
pnCentro.add(new PanelVS()); //PanelVS no requiere referencia, no se interactúa con ella.
pnCentro.add(pnPok2);
add(pnCentro, BorderLayout.CENTER);

setTitle("Batalla Pokemon");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}

private class AccionAtacar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {

if (pok1.suTurno) {
pok2.perderVida(pok1.atacar());
pnMensajes.setMensaje(pok1.mensaje);
pok1.suTurno = false;
pok2.suTurno = true;
}
else {
pok1.perderVida(pok2.atacar());
pnMensajes.setMensaje(pok2.mensaje);
pok1.suTurno = true;
pok2.suTurno = false;
}

pnPok1.actualizarPanel();
pnPok2.actualizarPanel();

if (pok1.getVida() <= 0 || pok2.getVida() <= 0) {
String mensajeFinal = "Juego Terminado\n Ha ganado: ";
mensajeFinal += pok1.getVida()>0?pok1.getNombre():pok2.getNombre();

JOptionPane.showMessageDialog(null, mensajeFinal, "Fin del Juego",
JOptionPane.INFORMATION_MESSAGE);

//Valores por defecto para volver a jugar
pok1.setVida(100);
pok1.suTurno = true;
pok2.setVida(100);
pok2.suTurno = true;
pnPok1.actualizarPanel();
pnPok2.actualizarPanel();
pnMensajes.setMensaje("¡Comienza la batalla!");
}


}
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new BatallaSwing();
}
});
}

}

Y esto es a grandes rasgos el funcionamiento del programa y la composición básica de la interfaz gráfica.
Respecto a esta, no entro a explicar cada detalle ni cada elemento porque sería extenderse mucho y no creo que sirva para aprender. Insisto que solo practicando y experimentando por uno mismo conseguimos familiarizarnos con la librería Swing.

Pero si tienes alguna pregunta concreta, adelante, no te cortes.

Un saludo.

526
Hola.
Para pasarlo a interfaz gráfica necesitas manejarte con la librería Swing de Java.
Y para manejarte bien con ella conviene tener dominado el tema de POO.

La librería Swing no es difícil, pero sí extensa y se necesita practicar mucho con ella. No es algo que se pueda explicar en un par de mensajes en el foro.
Hay que buscarse un tutorial, practicar, y sobre todo.. equivocarse mucho y atascarse mucho... porque es cuando más se aprende.

No se si tu intento con NetBeans fue usando el asistente para crear interfaces, que te lo facilita mucho (demasiado) simplemente arrastrando los componentes (botones, campos de texto,..) que quieres mostrar.
No recomiendo aprender Swing usando este tipo de asistentes. Son útiles para ahorrar tiempo cuando YA se ha aprendido y se sabe lo que se está haciendo.

Pero no para aprender, porque NetBeans te va generando montones de código que un humano no entiende... y al final tienes un programa de montones de líneas que no ha escrito uno mismo.
Yo de hecho nunca uso estos asistentes, aunque bueno, no me dedico a esto. Soy otro aprendiz que en sus (escasos) ratos libres aprende Java por ocio, por diversión....

He creado un nuevo proyecto con una interfaz Swing. Es un programa también sencillo y con código escrito rapidito (se me hace muy tarde en la noche y mañana trabajo  :o )

Usa la misma clase Pokemon que puse antes, con algunas modificaciones.
La interfaz es muy simple, he puesto algunos colores y distintos tipos de bordes para los paneles, para que sirvan un poco como ejemplo de uso.
No es muy bonito, pero bueno..



Aunque por mucho que leas y mires el código, no se si servirá para aprender.
Para aprender Swing (o JavaFX, o lo que sea...), hay que empezar desde el principio, con ejercicios sencillos.

Dejo este enlace a Google Drive para descargar el proyecto en un archivo zip. El código también se puede copiar y pegar desde este mismo hilo (ver más abajo).

Mañana si puedo, intentaré dar algunas explicaciones del código.
Un saludo.

527
Hola.
Primero quiero destacar que soy un ignorante absoluto del mundo Pokemon, así que he hecho un programa treméndamente simple je je..

He creado una clase Pokemon con dos atributos, nombre (String) y vida (int).
El valor de estos atributos se establece mediante el constructor.

Hay un tercer atributo que es simplemente un objeto Random para generar valores de ataque al azar.

Uno de los métodos que tiene, es el método atacar() que retornará un valor int que representa el daño conseguido en su turno de ataque.

Este método primero genera un valor aleatorio entre 10 y 20.
Luego, le pregunta a otro método de tipo boolean si este ataque es crítico o no.
Si es crítico, informa en pantalla e incrementa el valor de ataque en un 150%.
Por último, informa en pantalla del valor de ataque final (se haya incrementado o no) y retorna este valor.

¿Y cómo decidimos si este ataque es crítico o no?
El enunciado dice que hay un 10% de probabilidades.
Bien, las probabilidades (o las matemáticas en general) no son lo mío así que no se cuál es la forma más óptima de generar esas probabilidades.

A mí lo que se me ha ocurrido es generar un valor int entre 0 y 100. Si el valor generado es múltiplo de 10, pues retorno true, de lo contrario retorno false.
¿Y por qué hacerlo así?
Porque entre 0 y 100 solo hay diez valores que sean múltiplos de 10, es decir, hay un 10% que son múltiplos de 10 y un 90% que no lo son.
Por lo tanto, si hemos obtenido al azar un múltiplo de 10, pues es lo más parecido a generar una probabilidad del 10%....

Hay otro método llamado perderVida(), que simplemente resta al nivel de vida el valor de ataque generador por un Pokemon rival.

Luego hay un par de getters y un método toString(), en fin, como he dicho, es muy simple y no necesita mayores explicaciones.
Esta sería la clase Pokemon:
Código: [Seleccionar]
public class Pokemon {

private String nombre;
private int vida;
private Random azar = new Random();

public Pokemon(String nombrePok, int vidaPok) {
nombre = nombrePok;
vida = vidaPok;
}

public String getNombre() {
return nombre;
}

public int getVida() {
return vida;
}

public int atacar() {

int ataque = azar.nextInt(11) + 10;

if (esCritico()) {
System.out.println("¡" + nombre + " ha conseguido un ataque crítico!");
ataque =  (int)(ataque * 1.5);
}

System.out.println("Daño conseguido: " + ataque + " puntos");
return ataque;
}

public void perderVida(int ataque) {
vida -= ataque;
}

private boolean esCritico() {
int valor = azar.nextInt(100);
/*
* Entre 0 y 100 hay diez valores que son
* múltiplos de 10.
* Es decir, hay un 10% de posibilidades
* de obtener un valor múltiplo de 10.
*/

return valor%10 == 0;
}

@Override
public String toString() {
return "Nombre: " + nombre + "\nVida: " + vida;
}

}


Luego, para el combate, en la clase principal lo que hago es declarar dos objetos Pokemon e inicio un bucle while que se va a repetir mientras ambos contendientes tengan un nivel de vida superior a 0.
Si alguno pierde toda su vida, el bucle se detiene (el combate finaliza)

Dentro del bucle, pues voy informando de los datos de los contendientes y hay dos turnos de ataque, uno para cada Pokemon.

Cada vez que uno ataca, compruebo si el otro sigue vivo o no. Si sigue vivo, pasamos al turno en el que a este le toca atacar.
Si ha muerto, entonces no hay más turnos y todo termina.

Para poder ir leyendo en pantalla tranquilamente que ocurre en cada turno, utilizo un método llamado pausa() donde simplemente uso un Scanner para que el programa se detenga hasta que el usuario pulse la tecla enter.

Código: [Seleccionar]
public class Batalla {

private static Scanner teclado = new Scanner(System.in);

public static void main(String[] args) {

Pokemon pok1 = new Pokemon("Bulbasur", 100);
Pokemon pok2 = new Pokemon("Pikachu", 100);


while (pok1.getVida() > 0 && pok2.getVida() > 0) {
System.out.println("\nContendientes:");
System.out.println(pok1);
System.out.println(pok2);
pausa();
System.out.println("Turno de ataque para " + pok1.getNombre());
pok2.perderVida(pok1.atacar());
pausa();
if (pok2.getVida() <= 0) {
System.out.println(pok2.getNombre() + "ha sido derrotado.\n");
}
else {
//Turno del segundo pokemon
System.out.println("\nContendientes:");
System.out.println(pok1);
System.out.println(pok2);
pausa();
System.out.println("Turno de ataque para " + pok2.getNombre());
pok1.perderVida(pok2.atacar());
pausa();
if (pok1.getVida() <= 0)
System.out.println(pok1.getNombre() + " ha sido derrotado.\n");
}
}

System.out.println("\n\n\t\tFIN DEL JUEGO");


}

private static void pausa() {
System.out.println("\n\t\tPULSA ENTER PARA CONTINUAR\n");
teclado.nextLine();
}

}

Si lo ejecutamos, en pantalla podemos ver algo parecido a un combate por turnos:
Citar
Contendientes:
Nombre: Bulbasur
Vida: 100
Nombre: Pikachu
Vida: 100

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 10 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 100
Nombre: Pikachu
Vida: 90

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 15 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 85
Nombre: Pikachu
Vida: 90

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 17 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 85
Nombre: Pikachu
Vida: 73

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 12 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 73
Nombre: Pikachu
Vida: 73

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
¡Bulbasur ha conseguido un ataque crítico!
Daño conseguido: 27 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 73
Nombre: Pikachu
Vida: 46

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 15 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 58
Nombre: Pikachu
Vida: 46

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 20 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 58
Nombre: Pikachu
Vida: 26

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 15 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 43
Nombre: Pikachu
Vida: 26

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 15 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 43
Nombre: Pikachu
Vida: 11

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 14 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 29
Nombre: Pikachu
Vida: 11

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 12 puntos

      PULSA ENTER PARA CONTINUAR


Pikachu ha sido derrotado.



      FIN DEL JUEGO

529
De todo un poco... / Re: ¿Temperaturas altas de componentes?
« en: 02 de Febrero 2021, 11:35 »
Hola. En general me parecen temperaturas adecuadas.

Quizás la del SSD un pelín alta.
A ver, está dentro del rango normal. Un SSD oscila entre 30º y 50º según la carga de trabajo.
A partir de 60º sí que comenzaría a hacer throttling y si alcanzase los 70º las celdas de datos empezarían a estar en peligro.

Así que 41º no es nada preocupante, pero si dices que está en reposo y teniendo un gabinete tan despejado por dentro, quizás lo esperable sería tener una temperatura inferior.

Veo que has puesto un ventilador para el SSD y por la foto creo que ese ventilador estaría "aspirando" el calor.
Sin embargo, los SSD no son muy buenos disipando el calor que generan, así que quizás no haya mucho calor que aspirar alrededor del SSD.

Si te es posible, prueba a girar 180º el ventilador para invertir el flujo de aire y en lugar de aspirar lo que haga sea empujar aire hacia el SSD.
A ver si así notas alguna mejora en la temperatura.

No se si servirá, esto de la refrigeración a veces es una ciencia caprichosa je je.
Pero bueno, por probar...

530
Hola, no tengo mucha experiencia con esto de hacer lecturas binarias de ficheros.

Creo que una solución al primer ejercicio, podría ser esta:
Código: [Seleccionar]
public class LeerPDF {

public static void main(String[] args) {

try {
FileInputStream lectorBytes = new FileInputStream("D:\\Programacion\\FicherosTXT\\cita.pdf");
FileOutputStream escritorBytes = new FileOutputStream("D:\\Programacion\\FicherosTXT\\caracteresPDF.txt");

//Leemos todos los bytes del archivo PDF y los recibimos en un array
byte[] bytesLeidos =  lectorBytes.readAllBytes();

//Recorremos todos los bytes y si es el valor deseado, lo escribimos en el archivo texto
for (int i = 0; i < bytesLeidos.length; i++) {
byte esteByte = bytesLeidos[i];
if ((esteByte >= 32 && esteByte <= 127) || esteByte == 10 || esteByte == 13) {
escritorBytes.write(esteByte);
}
}

System.out.println("Caracteres extraidos...");
lectorBytes.close();
escritorBytes.close();

} catch (FileNotFoundException e) {
System.out.println("No se encuentra archivo");
e.getMessage();
} catch (IOException e) {
System.out.println("No se puede acceder al archivo");
e.getMessage();
}
}

}

531
A ver, para que no caiga esto en el olvido y sirva también de ayuda para futuros visitantes.

Resolvámoslo paso a paso.

Lo primero es conseguir leer el fichero y mostrar sus líneas.
Para esto podemos usar la clase Scanner o la clase BufferedReader.
Yo prefiero esta última.
Para crear un BufferedReader, necesitamos pasarle por constructor un objeto FileReader, quién a su vez necesita un objeto File cuya ruta apunte al fichero que queremos leer. También podemos pasarle directamente un String con esa ruta.

Una vez tenemos el BufferedReader, con el método newLine() le pedimos que nos lea la primera línea y la guardamos en un String
Y cada vez que invoquemos este método, nos devolverá la línea siguiente.

Así que para leer todas las líneas, usamos un bucle que se repetirá mientras que el String que nos devuelva el método newLine(), sea distinto de null, lo cuál significa que ya no quedan líneas por leer.

Cada línea obtenida, la mostramos en pantalla.

Importante que para este proceso, el compilador Java nos OBLIGA a usar try catch para capturar posibles excepciones como que el fichero que queremos leer no existe.
O que tal vez sí existe, pero no se puede leer porque está dañado o hay restricciones de acceso por permisos de usuario.
Java considera que trabajar con ficheros de datos es un tema sensible y no permite dejar sin controlar excepciones que pudieran originar pérdida de datos.

Código: [Seleccionar]
public class LeerFichero {

public static void main(String[] args) {
//Ruta donde encontrar el fichero de texto
String rutaTXT = "D:\\Programacion\\FicherosTXT\\restaurante.txt";

try {
//Configuramos lector de ficheros
BufferedReader lector = new BufferedReader(new FileReader(rutaTXT));
//Leemos línea
String linea = lector.readLine();
//Mientras la linea sea distinto de null, es decir, hay líneas para leer
while (linea != null) {
//Mostramos línea en pantalla
System.out.println(linea);
//Leemos siguiente línea, si no es null, se repetirá el bucle
linea = lector.readLine();
}
//Lectura terminada, cerramos lector
lector.close();
} catch (FileNotFoundException e) {
System.out.println("No se encuentra fichero: " + rutaTXT);
} catch (IOException e) {
System.out.println("No se puede acceder a " + rutaTXT);
}

}
}

Con este simple código, en pantalla podremos ver todas las líneas del fichero:
Citar
Restaurant,Address,City,State,Zipcode
Vietnam Restaurant,1615 Commerce Parkway,Bloomington,IL,61704
Chops and Ribs,71 S. Wacker Drive Chicago,Chicago,IL,60606
Molinarinari,"1701 River Drive, Suite 306",Moline,IL,61265
Siam Cusine,"3201 W White Oaks Drive, Suite 204",Springfield,IL,62704
Mary's Diner,"4916 S Technopolis Drive, Building 2",Sioux Falls,SD,57106
Bistro Bob,625 32nd Avenue SW,Cedar Rapids,IA,52404
Wiener House,"400 Locust Street, Suite 420 4th Floor",Des Moines,IA,50309
Irifune Place,"Federal Trust Building, 134 South 13th Street, 10th Floor, Suite 1000",Lincoln,NE,68508
Estradanas,"1111 N 102nd Court, Suite 231",Omaha,NE,68114
Fargo,"3310 Fiectner Drive, Suite 108",Fargo,ND,58103
Fangs,650 Third Avenue South,Minneapolis,MN,55402
China Palace,3605 Hwy 52 North,Rochester,MN,55901
Big Daddy Barbeque,"1005 Ikon Drive, Suite G,",Jefferson City,MO,65109
Wolfs,"3315 E. Ridgeview Street, Suite 4000",Springfield,MO,65804
A Cut Above,30501 Agoura Road,Agoura Hills,CA,91301
Ranch House,650 Harry Road,San Jose,CA,95120
Burgers and Fries,5100 California Avenue,Bakersfield,CA,93309
Hofbrau Deluxe,600 Anton Blvd,Costa Mesa,CA,92626
Jean's Place,100 N. Sepulveda Blvd,El Segundo,CA,90245
Nattos,1001 E. Hillsdale Blvd,Foster City,CA,94404


Bien.
Vemos que cada línea contiene 5 datos (restaurante, dirección, ciudad, estado, código postal).
Estos datos están separados unos de otros mediante "comas", así que podemos obtener esos datos por separado "cortando" el String allá donde exista una coma.

La clase String ofrece el método split() que nos ayuda a hacer esto y nos retorna los "trocitos" (los datos) separados, pero agrupados en un array.

Código: [Seleccionar]
public class LeerFichero {

public static void main(String[] args) {
//Ruta donde encontrar el fichero de texto
String rutaTXT = "D:\\Programacion\\FicherosTXT\\restaurante.txt";

try {
//Configuramos lector de ficheros
BufferedReader lector = new BufferedReader(new FileReader(rutaTXT));
//Leemos línea
String linea = lector.readLine();
//Mientras la linea sea distinto de null, es decir, hay líneas para leer
while (linea != null) {
//Separamos los datos en un array
String[] datos = linea.split(",");
//Mostramos el array de datos para comprobar su correcta separacion
System.out.println(Arrays.toString(datos));
//Leemos siguiente línea, si no es null, se repetirá el bucle
linea = lector.readLine();
}
//Lectura terminada, cerramos lector
lector.close();
} catch (FileNotFoundException e) {
System.out.println("No se encuentra fichero: " + rutaTXT);
} catch (IOException e) {
System.out.println("No se puede acceder a " + rutaTXT);
}

}
}

Si mostramos en pantalla el contenido de los arrays que split nos va proporcionando, veremos que tenemos los mismos datos que veíamos en las líneas, pero esta vez contenidos en arrays y como elementos separados.
Citar
[Restaurant, Address, City, State, Zipcode]
[Vietnam Restaurant, 1615 Commerce Parkway, Bloomington, IL, 61704]
[Chops and Ribs, 71 S. Wacker Drive Chicago, Chicago, IL, 60606]
[Molinarinari, "1701 River Drive,  Suite 306", Moline, IL, 61265]
[Siam Cusine, "3201 W White Oaks Drive,  Suite 204", Springfield, IL, 62704]
[Mary's Diner, "4916 S Technopolis Drive,  Building 2", Sioux Falls, SD, 57106]
[Bistro Bob, 625 32nd Avenue SW, Cedar Rapids, IA, 52404]
[Wiener House, "400 Locust Street,  Suite 420 4th Floor", Des Moines, IA, 50309]
[Irifune Place, "Federal Trust Building,  134 South 13th Street,  10th Floor,  Suite 1000", Lincoln, NE, 68508]
[Estradanas, "1111 N 102nd Court,  Suite 231", Omaha, NE, 68114]
[Fargo, "3310 Fiectner Drive,  Suite 108", Fargo, ND, 58103]
[Fangs, 650 Third Avenue South, Minneapolis, MN, 55402]
[China Palace, 3605 Hwy 52 North, Rochester, MN, 55901]
[Big Daddy Barbeque, "1005 Ikon Drive,  Suite G, ", Jefferson City, MO, 65109]
[Wolfs, "3315 E. Ridgeview Street,  Suite 4000", Springfield, MO, 65804]
[A Cut Above, 30501 Agoura Road, Agoura Hills, CA, 91301]
[Ranch House, 650 Harry Road, San Jose, CA, 95120]
[Burgers and Fries, 5100 California Avenue, Bakersfield, CA, 93309]
[Hofbrau Deluxe, 600 Anton Blvd, Costa Mesa, CA, 92626]
[Jean's Place, 100 N. Sepulveda Blvd, El Segundo, CA, 90245]
[Nattos, 1001 E. Hillsdale Blvd, Foster City, CA, 94404]

Aquí cabe destacar una cosa. Algunas líneas tienen direcciones compuestas que incorporan una coma, por ejemplo: "3315 E. Ridgeview Street,  Suite 4000"
En estos casos, en lugar de obtener un array de 5 elementos, nos dará un array de 6 elementos, porque esa address compuesta también será "troceada" por el método split().

Para este ejercicio, nos da igual que esto ocurra, porque solo nos va a interesa el último elemento, el zipcode.
Pero si nos pidieran trabajar también con el dato address, habría que tener en cuenta que en ocasiones este dato lo íbamos a obtener separado en dos elementos.

Bueno, dicho esto, el ejercicio nos pide mostrar los datos únicamente de los restaurantes cuyo zipcode comience por 6.
Puesto que ya tenemos los datos separados, para hacer esto nos basta con preguntar si el último elemento de cada array, que es donde se encuentra el código postal, comienza por 6.
Y si esto se cumple, pues mostramos los datos en pantalla.

Código: [Seleccionar]
public class LeerFichero {

public static void main(String[] args) {
//Ruta donde encontrar el fichero de texto
String rutaTXT = "D:\\Programacion\\FicherosTXT\\restaurante.txt";

try {
//Configuramos lector de ficheros
BufferedReader lector = new BufferedReader(new FileReader(rutaTXT));
//Leemos línea
String linea = lector.readLine();
//Mientras la linea sea distinto de null, es decir, hay líneas para leer
while (linea != null) {
//Separamos los datos en un array
String[] datos = linea.split(",");

//Preguntamos si último elemento, el zipcode comienza por 6
if (datos[datos.length - 1].startsWith("6"))
System.out.println(Arrays.toString(datos)); //Mostramos restaurante

//Leemos siguiente línea, si no es null, se repetirá el bucle
linea = lector.readLine();
}
//Lectura terminada, cerramos lector
lector.close();
} catch (FileNotFoundException e) {
System.out.println("No se encuentra fichero: " + rutaTXT);
} catch (IOException e) {
System.out.println("No se puede acceder a " + rutaTXT);
}

}
}

De este modo, ahora en pantalla solo vemos los restaurantes que comienzan por 6:
Citar
[Vietnam Restaurant, 1615 Commerce Parkway, Bloomington, IL, 61704]
[Chops and Ribs, 71 S. Wacker Drive Chicago, Chicago, IL, 60606]
[Molinarinari, "1701 River Drive,  Suite 306", Moline, IL, 61265]
[Siam Cusine, "3201 W White Oaks Drive,  Suite 204", Springfield, IL, 62704]
[Irifune Place, "Federal Trust Building,  134 South 13th Street,  10th Floor,  Suite 1000", Lincoln, NE, 68508]
[Estradanas, "1111 N 102nd Court,  Suite 231", Omaha, NE, 68114]
[Big Daddy Barbeque, "1005 Ikon Drive,  Suite G, ", Jefferson City, MO, 65109]
[Wolfs, "3315 E. Ridgeview Street,  Suite 4000", Springfield, MO, 65804]


532
Hola.

La ordenación que te hace es "correcta", porque al ser un String te hace una comparación lexicográfica.

Pero da igual si es correcto o no, lo que importa es que tú, el programador, quieres una comparación numérica, a pesar de que el dato no es exactamente numérico.

En este caso, la única solución es trabajar con el String para "enseñarle" a la clase Jugador cómo ha de compararse.

Habrá que hacer un método compareTo() más elaborado.
Establecemos que formato del ID va a ser siempre "jxx", es decir, siempre va a empezar por el carácter 'j' y a continuación tendrá uno o más caracteres que en unos casos podrán ser letras y en otros pueden ser números.

Pues antes de empezar a comparar habrá que comprobar que tipo de ID tienen los objetos Jugador que vamos a comparar.

Si ambos tienen id's con números, habrá que hacer una comparación numérica.
Pero si solo uno tiene id numérico, o no lo tienen ninguno de los dos, entonces habrá que hacer una comparación lexicográfica normal.

Para poder distinguir fácilmente entre un tipo de id del otro, podemos hacer un método de tipo boolean, que si detecta caracteres numéricos en el id, retorna true.

Código: [Seleccionar]
private boolean idContieneNumeros(String id) {

for (int i = 0; i < id.length(); i++) {
switch(id.charAt(i)) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return true; //Se han encontrado caracteres numéricos
}
}
//Si el bucle termina sin retorna true...
return false; //...es que no hay caracteres numéricos
}


Y ahora que podemos distinguir un id del otro, podemos hacer que el compareTo() decida que tipo de comparación ha de realizar.
Si detecta que AMBOS id son numéricos, lo que hay que hacer es crear a partir de ellos un nuevo String quitando la 'j' del principio.
De este modo, este nuevo String se supone que solo son caracteres numéricos, así que podemos hacer un parseo y convertirlos a tipo int.
Y una vez convertidos, ya solo comparamos quien es mayor o menor y devolvemos el valor correspondiente. (El método compareTo() lo que hace es devolver un entero positivo o negativo, según quien es mayor o menor)

El método podría quedar así. Incluye comentarios explicando cada paso:
Código: [Seleccionar]
public int compareTo(Jugador jug) {
//Comprobamos si AMBOS Jugador tienen id con numéros
if (idContieneNumeros(id) && idContieneNumeros(jug.id)) {
/*
* Los id se compone del carácter 'j' seguido de uno o más
* caracteres numéricos.
* Crearemos dos nuevos String solo con los números de los id
*/
String numerosDeEsteJugador = id.substring(1); //Descartamos primer carácter
String numerosDeOtroJugador = jug.id.substring(1);
/*
* Intentaremos convertirlos a valores enteros.
* Si la conversión fallase, quizás porque un ID no respeta
* el formato "jxx" y tiene más letras entre los números, entonces
* devoveremos una comparación lexicográfica normal
*/
try {
int valorDeEste = Integer.parseInt(numerosDeEsteJugador);
int valorDeOtro = Integer.parseInt(numerosDeOtroJugador);
//Tenemos enteros numéricos, comparamos
if (valorDeEste < valorDeOtro)
return -1; //Este es MENOR
else if (valorDeEste > valorDeOtro)
return 1; //Este es MAYOR
else
return 0; //Ambos son iguales,no debería ocurrir,porque significaría que tienen mismo id
}
catch(NumberFormatException nfe) {
return id.compareTo(jug.id); //Conversión numérica ha fallado
}

}
else//Solo uno, o ninguno, tiene id con números
return id.compareTo(jug.id);//Devolvemos comparación lexicográfica normal
}


Con esto, he creado mi propia clase Jugador:
Código: [Seleccionar]
public class Jugador implements Comparable<Jugador>{

private String id;
private String nombre;

public Jugador(String id, String nombre) {
this.id = id;
this.nombre = nombre;
}

@Override
public String toString() {
return "ID: " + id + " Nombre: " + nombre;
}

@Override
public int compareTo(Jugador jug) {
//Comprobamos si AMBOS Jugador tienen id con numéros
if (idContieneNumeros(id) && idContieneNumeros(jug.id)) {
/*
* Los id se compone del carácter 'j' seguido de uno o más
* caracteres numéricos.
* Crearemos dos nuevos String solo con los números de los id
*/
String numerosDeEsteJugador = id.substring(1); //Descartamos primer carácter
String numerosDeOtroJugador = jug.id.substring(1);
/*
* Intentaremos convertirlos a valores enteros.
* Si la conversión fallase, quizás porque un ID no respeta
* el formato "jxx" y tiene más letras entre los números, entonces
* devoveremos una comparación lexicográfica normal
*/
try {
int valorDeEste = Integer.parseInt(numerosDeEsteJugador);
int valorDeOtro = Integer.parseInt(numerosDeOtroJugador);
//Tenemos enteros numéricos, comparamos
if (valorDeEste < valorDeOtro)
return -1; //Este es MENOR
else if (valorDeEste > valorDeOtro)
return 1; //Este es MAYOR
else
return 0; //Ambos son iguales,no debería ocurrir,porque significaría que tienen mismo id
}
catch(NumberFormatException nfe) {
return id.compareTo(jug.id); //Conversión numérica ha fallado
}

}
else//Solo uno, o ninguno, tiene id con números
return id.compareTo(jug.id);//Devolvemos comparacion lexicográfica normal
}

private boolean idContieneNumeros(String id) {

for (int i = 0; i < id.length(); i++) {
switch(id.charAt(i)) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return true; //Se han encontrado caracteres numéricos
}
}
//Si el bucle termina sin retorna true...
return false; //...es que no hay caracteres numéricos
}

}


Y en la siguiente clase principal, he creado tres ArrayList para poner a prueba el algoritmo de ordenación.
Uno tiene jugadores con id numéricos, otro jugadores con id de solo letras y el tercero tiene ambos tipos de jugadores.

Código: [Seleccionar]
public class Ordenar {

public static void main(String[] args) {

System.out.println("Ordenación solo IDs con números");
ArrayList<Jugador> jugadores = new ArrayList<Jugador>();
jugadores.add(new Jugador("j3","pepe"));
jugadores.add(new Jugador("j20","juan"));
jugadores.add(new Jugador("j1","maria"));
jugadores.add(new Jugador("j10","maria"));

jugadores.sort(null);

for (Jugador jug: jugadores)
System.out.println(jug);

System.out.println("\nOrdenación solo IDs con letras");
ArrayList <Jugador> jugadores2 = new ArrayList<Jugador>();
jugadores2.add(new Jugador("jA","pepe"));
jugadores2.add(new Jugador("jD","juan"));
jugadores2.add(new Jugador("jC","maria"));
jugadores2.add(new Jugador("jB","maria"));

jugadores2.sort(null);

for (Jugador jug: jugadores2)
System.out.println(jug);

System.out.println("\nOrdenación con ambos tipos de ID");
ArrayList<Jugador> jugadoresTodos = new ArrayList<Jugador>();
jugadoresTodos.add(new Jugador("j3","pepe"));
jugadoresTodos.add(new Jugador("jA","pepe"));
jugadoresTodos.add(new Jugador("j20","juan"));
jugadoresTodos.add(new Jugador("jD","juan"));
jugadoresTodos.add(new Jugador("j1","maria"));
jugadoresTodos.add(new Jugador("jC","maria"));
jugadoresTodos.add(new Jugador("j10","maria"));
jugadoresTodos.add(new Jugador("jB","maria"));

jugadoresTodos.sort(null);

for (Jugador jug: jugadoresTodos)
System.out.println(jug);

}

}

Y en los tres casos, parece que hace la ordenación que deseábamos:

Citar
Ordenación solo IDs con números
ID: j1 Nombre: maria
ID: j3 Nombre: pepe
ID: j10 Nombre: maria
ID: j20 Nombre: juan

Ordenación solo IDs con letras
ID: jA Nombre: pepe
ID: jB Nombre: maria
ID: jC Nombre: maria
ID: jD Nombre: juan

Ordenación con ambos tipos de ID
ID: j1 Nombre: maria
ID: j3 Nombre: pepe
ID: j10 Nombre: maria
ID: j20 Nombre: juan
ID: jA Nombre: pepe
ID: jB Nombre: maria
ID: jC Nombre: maria
ID: jD Nombre: juan

533
Hola.
¿En qué tienes dificultades?
No podemos ayudarte si no nos dices que necesitas saber.

Comienza tú un código y cuando te atasques te ayudamos a completarlo.

Intenta hacerlo paso a paso, no pensando en cómo solucionar el TODO, si no buscando objetivos menores.
Si te han pedido este ejercicio, es que como mínimo te han explicado como leer ficheros de texto.
Pues comienza haciendo un programa que lea ese fichero de texto y muestre sus líneas en pantalla.

Cuando tengas eso, modifícalo para que en lugar de mostrar las líneas, separes los datos que contiene cada línea delimitados por las comas.
La clase String ofrece un método llamado split() que trocea una cadena de texto "cortando" donde encuentre el carácter que tu le indiques y te retorna los "trozos" en un array de String.
Así que puedes hacer algo como:
Código: [Seleccionar]
String[] datos = linea.split(",");Así cada línea leída, puedes "trocearla" y consultar esos datos por separado.
Inténtalo y haz que solo se muestre el código postal de cada línea.

Cuando tengas eso, haz que solo se muestren los códigos postales que empiecen por 6.

Y teniendo eso, ya solo es modificarlo un poco para que muestre todos los datos, de las líneas cuyo código postal comience por 6.


Insisto, inténtalo tú. Llega hasta donde puedas, y a partir de ahí te ayudamos.

534
En el otro tema donde te presentaste te he enlazado algunos cursos disponibles en esta web.

A parte de esto, el único consejo es que pruebes todas las modalidades de aprendizaje hasta encontrar la que más te guste.
Aunque lo más probable es que acabes combinando.

Tener PDF's de programación, es cierto que demasiada lectura puede cansar, pero está bien tenerlos a mano porque pueden servir de referencia rápida cuando quieres recordar como se hacía tal cosa.
Incluso un ejercicio de aprendizaje útil es coger las partes que te resulten más interesantes de un PDF y crear tu propio documento de referencia.
Así obtienes un documento más pequeño y resumido, y como lo has creado tú, está más adaptado a tu forma de pensar.

Los vídeos de Youtube también son muy útiles. Hay gente que no los recomienda, pero me parece absurdo.
Se hace más ameno escuchar una voz y ver como lo hace el Yotuber y que pasos está siguiendo.
Eso sí, recomiendo buscar videotutoriales hechos con vídeos que no duren más de 20/25 minutos. Pasado ese tiempo a las personas pierden capacidad para seguir prestando atención...

Hay algunos que explican todo en vídeos de hasta 2 horas...  :o

Ni hablar.., es mejor tener que ver 60/70 vídeos de 15 minutos, que no solo 4 vídeos de 2 horas.

536
Comunidad / Re: Hola a todos y espero que seamos buenos amigos
« en: 14 de Enero 2021, 11:43 »
Hola, estás en tu casa.
Por poco que sepas, bienvenido será.

Yo por ejemplo no se nada VB, así que quien necesite ayuda con eso tu resultarás mucho más útil que yo.

Por cierto, aquí en la web hay algunos cursos a los que puedes acceder para afianzar tus conocimientos en VB y/o iniciarte en otros lenguajes.

Un saludo.

537
Aprender a programar desde cero / Re: Ayuda en C++
« en: 14 de Enero 2021, 01:22 »
No se si responderá porque fue alguien que publicó sin registrarse en el foro.

Pero sí, al decir subprogramas, se refiere a las funciones.
También se les llama (o antaño se les solía llamar..) subrutinas.

Funciones y procedimientos son como pequeños programas, dentro del programa principal, por eso también reciben nombres como los que hemos mencionado.

538
Comunidad / Re: Agradecimiento.
« en: 12 de Enero 2021, 11:23 »
Bienvenido.
Cualquier cosa que puedas aportar siempre se agradecerá.

Un saludo.

539
Si ejecutas tu código, verás que no funciona.
Al margen de unas pequeñas correciones necesarias en la sintaxis, cuando el programa inicia solo conseguimos un bucle infinito.
El for ni siquiera funciona, porque usas la variable n como límite, la cuál no tiene ningún valor inicial, por lo que toma valor 0 así que el for no llega a activarse.

Pero es que de todos modos no necesitas ningún bucle for.

Solo necesitas un bucle do while, que es el que se te repite infinitamente por la condición que has puesto:
Código: [Seleccionar]
while (opcion[0])
¿Qué significa esa condición?
Las condiciones de los bucles han de ser expresiones que den como resultado true o false.
opcion[0] es simplemente el primer char que contiene el array llamado opcion,

Eso no te sirve como condición del while. Y no necesitas ningún array de char para nada.


Fíjate en la condición que puse yo en mi bucle while:
Código: [Seleccionar]
while (calificacion != 0);
"Mientras calificación sea distinto de 0".

¿Por qué esa condición?
Porque eso es lo que pide el enunciado:
Citar
programa que lea calificaciones del 5 al 10, hasta que se le capture un 0
Es decir, leemos calificaciones (en una variable llamada calificación) y MIENTRAS lo leído sea distinto de 0, seguiremos pidiendo calificaciones.

Tampoco necesitas ninguna función para calcular el promedio.
Quizás me equivoco, pero parece que hayas encontrado el código de esa función por algún sitio y lo has copiado con la esperanza de que te sirva.

No uses nunca código que no entiendas. Porque viendo el código de esa función es evidente que no es aplicable para este ejercicio.

Mira, añado al código que puse las líneas necesarias para calcular el promedio.
¡¡Solo he necesitado añadir 3 líneas!! (Las resalto en color azul)

Citar
#include <iostream>

using namespace std;

int main()
{
    int calificacion = 0; //Para leer calificaciones por teclado
    int contador = 0; //Para contar las calificaciones introducidas
    int suma = 0; //Aquí sumamos calificaciones para luego calcular promedio.

    do {
        cout << "\nIntroduzca calificación: ";
        cin >> calificacion;

        if (calificacion != 0) {
            if (calificacion >= 5 && calificacion <= 10) {
                //Calificación es válida
                contador++; //Contamos la calificación introducida
                suma = suma + calificacion; //Sumamos la calificación
            }
            else
                cout << "Calificación no es válida, solo valores entre 5 y 10\n";
        }
        else //Ha introducido 0, fin del bucle
            cout << "\nFin de lectura de calificaciones\n";
    } while (calificacion != 0);

    //Resultados
    cout << "\nCalificaciones introducidas: " << contador;
    cout << "\nNota promedio: " << (suma/(float)contador);

    cout << "\n\t\tFIN DE PROGRAMA";
}

Puedes verlo y ejecutarlo desde este enlace:
https://www.online-ide.com/9nyPO5lxRG

Fíjate que no ha sido necesario usar ningún array (por ahora), ni ninguna función.

Analiza el código que he compartido. Asegúrate de que entiendes cada línea y que puedes seguir el flujo que sigue el programa.
Te dejo un diagrama de flujo (no se me dan muy bien) para que se entienda mejor la lógica del programa.

Cuando tengas todo claro, podemos pasar a ver como informar en la salida de resultados cuál ha sido la calificación MAYOR introducida.


540
Mira, por si te sirve para arrancar.

Dejo un código que pide calificaciones hasta que se introduzca 0.
Si está entre 5 y 10, es válida y la cuenta.
Si no, informa de que no es valida y no se cuenta.

Al terminar el bucle, informa de cuántas calificaciones válidas se han introducido.

Desde este enlace puedes probar el código online, e incluso modificarlo:
https://www.online-ide.com/5vThQyewI2


Código: [Seleccionar]
#include <iostream>

using namespace std;

int main()
{
    int calificacion = 0;
    int contador = 0;

    do {
        cout << "\nIntroduzca calificación: ";
        cin >> calificacion;

        if (calificacion != 0) {
            if (calificacion >= 5 && calificacion <= 10) {
                //Calificación es válida
                contador++; //Contamos la calificación introducida
            }
            else
                cout << "Calificación no es válida, solo valores entre 5 y 10\n";
        }
        else //Ha introducido 0, fin del bucle
            cout << "\nFin de lectura de calificaciones\n";
    } while (calificacion != 0);

    //Resultados
    cout << "\nCalificaciones introducidas: " << contador;

    cout << "\n\t\tFIN DE PROGRAMA";
}


Paso siguiente sería añadir una variable "suma" e ir acumulando la suma de todas las calificaciones válidas.
Luego al final del programa, se podría calcular el promedio dividiendo la suma entre contador.

Páginas: 1 ... 22 23 24 25 26 [27] 28 29 30 31 32 ... 50

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".