Activitats

Ranquing orientat a byte

L’objectiu d’aquesta activitat és veure com modificar un mòdul de manera que no calgui modificar absolutament res de la resta del programa.

Modifiqueu la classe Ranquing, amb la qual heu treballat als continguts d’aquest apartat, de manera que ara, en lloc de treballar amb un fitxer orientat a caràcter, ho faci amb un orientat a byte. Les dades que ha de contenir són les mateixes (una llista amb conjunts de tres inicials i la puntuació associada). En aquest cas, però, no hi ha puntuacions per defecte a l’inici del joc. Primer de tot no hi ha fitxer, i el llistat va incorporant les puntuacions de successives partides a poc a poc, amb un màxim sempre de 10 alhora. Després de la primera partida hi haurà 1 puntuació, després de la segona 2, etc. Això vol dir que les 10 primeres puntuacions al llarg de la vida del joc sempre s’inclouran al fitxer.

En dur a terme aquesta tasca, cal mantenir els principis de modularitat. Això implica que no ha de caldre modificar absolutament cap altra classe llevat de Ranquing.

  1. package joc.arena.fitxers;
  2.  
  3. import java.io.File;
  4. import java.io.RandomAccessFile;
  5.  
  6. public class Ranquing {
  7.  
  8. //Es declara el fitxer de puntuacions com una constant
  9. public static final File RANQUING = new File("Ranquing");
  10.  
  11. /** A partir d'una puntuació, estableix la seva posició al fitxer
  12.   *
  13.   * @param punts Punts que cal comprovar
  14.   * @return posició per la puntuació. -1 Si error.
  15.   */
  16. public int cercarRanquing(int punts) {
  17. try {
  18. //Fitxer no existeix (primera partida)
  19. if (RANQUING.isFile() == false) {
  20. return 0;
  21. }
  22. RandomAccessFile raf = new RandomAccessFile(RANQUING,"r");
  23. //Cada posició ocupa 10 bytes (3 chars = 2 bytes) + (1 int = 4 bytes)
  24. long numPosicions = RANQUING.length() / 10;
  25. int pos = 0;
  26. while (pos < numPosicions) {
  27. //Saltar inicials i llegim punts
  28. raf.skipBytes(6);
  29. int rankPunts = raf.readInt();
  30. if (punts > rankPunts) {
  31. //S'ha trobat la posició
  32. break;
  33. }
  34. pos++;
  35. }
  36. //S'arriba aquí si s'ha arribat al final del fitxer
  37. raf.close();
  38. return pos;
  39. } catch(Exception e) {
  40. return -1;
  41. }
  42. }
  43.  
  44. /** Insereix una puntuació al rànquing
  45.   *
  46.   * @param inicials Inicials del jugador
  47.   * @param punts Puntuació assolida
  48.   * @param pos Posició dins el rànquing, o -1 si hi ha error
  49.   * @return 0 si tot correcte, -1 Si error.
  50.   */
  51. public int entrarPuntuacio(String inicials, int punts, int pos) {
  52. try {
  53. //Són realment inicials?
  54. if (inicials.length() != 3) {
  55. return -1;
  56. }
  57. RandomAccessFile raf = new RandomAccessFile(RANQUING,"rw");
  58. //Cada posició ocupa 10 bytes (3 chars = 2 bytes) + (1 int = 4 bytes)
  59. long numPosicions = RANQUING.length() /10;
  60. if (pos < numPosicions) {
  61. //Cal desplaçar rànquings
  62. for (long i = numPosicions; i > pos; i--) {
  63. long offset = (i - 1)*10;
  64. raf.seek(offset);
  65. //Es llegeixen tres inicials
  66. String inis = "" + raf.readChar();
  67. inis = inis + raf.readChar();
  68. inis = inis + raf.readChar();
  69. //Es llegeixen els punts
  70. int pun = raf.readInt();
  71.  
  72. //S'escriuen al bloc següent
  73. raf.writeChars(inis);
  74. raf.writeInt(pun);
  75. }
  76. }
  77. //Escriure puntuació
  78. long offset = pos*10;
  79. raf.seek(offset);
  80. raf.writeChars(inicials);
  81. raf.writeInt(punts);
  82. raf.close();
  83. return 0;
  84. } catch (Exception e) {
  85. return -1;
  86. }
  87. }
  88.  
  89. /** Llegeix les puntuacions i les formata com una cadena de text
  90.   *
  91.   * @return Cadena de text resultant. null si hi ha error
  92.   */
  93. public String llegirRanquing() {
  94. try {
  95. String txtRanquing = "Rànquing de puntuacions\n----------------------\n";
  96. RandomAccessFile raf = new RandomAccessFile(RANQUING,"r");
  97. long numPosicions = RANQUING.length() /10;
  98. for (int i = 0; i < numPosicions; i++) {
  99. txtRanquing = txtRanquing + raf.readChar();
  100. txtRanquing = txtRanquing + raf.readChar();
  101. txtRanquing = txtRanquing + raf.readChar();
  102. txtRanquing = txtRanquing + "\t" + raf.readInt() + "\n";
  103. }
  104. raf.close();
  105. return txtRanquing;
  106. } catch (Exception e) {
  107. return null;
  108. }
  109.  
  110. }
  111.  
  112. }

Editor de bestiari avançat

L’objectiu d’aquesta activitat és practicar l’accés a fitxers relatius.

Modifiqueu la classe EditorBestiari, presentada als continguts d’aquest apartat, de manera que ara, en eliminar un adversari del fitxer, no ho faci pel final. Ha de preguntar quina posició es vol eliminar (la primera és la 0) i esborrar només aquell adversari. El nou programa ha de garantir que l’usuari mai posa una posició incorrecta (un valor que no és enter o que no existeix al fitxer).

Tot seguit es mostra una sortida d’exemple del programa:

Nom del fitxer a editar: Adversaris
[0] Nan            :         Nivell: 1 (punts: 25)        VIDA: 8        ATAC: 3        DEFENSA: 3
[1] Kobold         :         Nivell: 1 (punts: 30)        VIDA: 10        ATAC: 4        DEFENSA: 2
[2] Orc            :         Nivell: 2 (punts: 35)        VIDA: 12        ATAC: 4        DEFENSA: 3
[3] Profund        :         Nivell: 2 (punts: 40)        VIDA: 14        ATAC: 3        DEFENSA: 4
[4] Bruixot Malvat :         Nivell: 3 (punts: 45)        VIDA: 15        ATAC: 3        DEFENSA: 5
[5] Ogre           :         Nivell: 3 (punts: 50)        VIDA: 16        ATAC: 5        DEFENSA: 2
[6] Guerrer Caòtic :         Nivell: 4 (punts: 55)        VIDA: 15        ATAC: 4        DEFENSA: 4
[7] Troll          :         Nivell: 4 (punts: 60)        VIDA: 18        ATAC: 3        DEFENSA: 5
[8] Elemental Terra:         Nivell: 5 (punts: 70)        VIDA: 22        ATAC: 4        DEFENSA: 6
[9] Hidra          :         Nivell: 5 (punts: 80)        VIDA: 30        ATAC: 8        DEFENSA: 2
------------------------------
[A]fegir        [E]liminar        [S]ortir
Acció: e
Posició a tractar [0 - 9]: 10
Aquesta no existeix. Posició a tractar [0 - 9]: -1
Aquesta no existeix. Posició a tractar [0 - 9]: 2
[0] Nan            :         Nivell: 1 (punts: 25)        VIDA: 8        ATAC: 3        DEFENSA: 3
[1] Kobold         :         Nivell: 1 (punts: 30)        VIDA: 10        ATAC: 4        DEFENSA: 2
[2] Profund        :         Nivell: 2 (punts: 40)        VIDA: 14        ATAC: 3        DEFENSA: 4
[3] Bruixot Malvat :         Nivell: 3 (punts: 45)        VIDA: 15        ATAC: 3        DEFENSA: 5
[4] Ogre           :         Nivell: 3 (punts: 50)        VIDA: 16        ATAC: 5        DEFENSA: 2
[5] Guerrer Caòtic :         Nivell: 4 (punts: 55)        VIDA: 15        ATAC: 4        DEFENSA: 4
[6] Troll          :         Nivell: 4 (punts: 60)        VIDA: 18        ATAC: 3        DEFENSA: 5
[7] Elemental Terra:         Nivell: 5 (punts: 70)        VIDA: 22        ATAC: 4        DEFENSA: 6
[8] Hidra          :         Nivell: 5 (punts: 80)        VIDA: 30        ATAC: 8        DEFENSA: 2
------------------------------
[A]fegir        [E]liminar        [S]ortir
Acció: e
Posició a tractar [0 - 8]: 
...

  1. //PARTS MODIFICADES
  2.  
  3. /** Elimina el darrer adversari en un fitxer.
  4.   *
  5.   * @param fitxer Ruta del fitxer a modificar
  6.   */
  7. public void eliminarAdversari(File fitxer) {
  8. try {
  9. RandomAccessFile raf = new RandomAccessFile(fitxer,"rw");
  10. if (fitxer.length() > MIDA_BYTES_ADV) {
  11. //Queda mes d'un element
  12. int pos = llegirPosicio(fitxer);
  13. long numAdv = fitxer.length()/MIDA_BYTES_ADV;
  14. //Desplaçar elements
  15. for (int i = pos + 1; i < numAdv; i++) {
  16. byte[] dades = new byte[MIDA_BYTES_ADV];
  17. //Es llegeixen 50 bytes i s'escriuen 50 posicions abans
  18. raf.seek(i*MIDA_BYTES_ADV);
  19. raf.read(dades);
  20. raf.seek((i-1)*MIDA_BYTES_ADV);
  21. raf.write(dades);
  22. }
  23.  
  24. //Un cop desplaçats, esborra darrer element (ara repetit)
  25. raf.setLength(fitxer.length() - MIDA_BYTES_ADV);
  26. } else {
  27. //Queda un element o cap, no val la pena preguntar res
  28. raf.setLength(0);
  29. }
  30. raf.close();
  31. } catch (Exception e) {
  32. System.out.println("Error esborrant dades al fitxer " + fitxer);
  33. }
  34. }
  35.  
  36. /** Llegeix un enter del teclat, corresponent a una posició d'un adversari
  37.   * al fitxer.
  38.   *
  39.   * @param fitxer Fitxer a tractar
  40.   * @return Posició a tractar
  41.   */
  42. public int llegirPosicio(File fitxer) {
  43. Scanner lector = new Scanner(System.in);
  44. long maxPos = fitxer.length()/MIDA_BYTES_ADV - 1;
  45. boolean llegir = true;
  46. int pos = 0;
  47. while (llegir) {
  48. System.out.print("Posició a tractar [0 - " + maxPos +"]: ");
  49. if (lector.hasNextInt()) {
  50. pos = lector.nextInt();
  51. if ((pos < 0)||(pos > maxPos)) {
  52. System.out.print("Aquesta no existeix. ");
  53. } else {
  54. llegir = false;
  55. }
  56. } else {
  57. System.out.print("No has escrit un enter. ");
  58. }
  59. }
  60. return pos;
  61. }

Desar partida

L’objectiu d’aquesta activitat és aprendre a afegir un nou mòdul a una aplicació ja existent.

Amplieu l’aplicació del joc de lluites a l’arena, usat al llarg dels continguts d’aquest apartat, i deseu la partida quan sigui possible. Quan el joc pregunta contra quin adversari lluitar, es pot usar l’ordre “desar”. En fer-ho, es desa l’estat de la partida en un fitxer anomenat “Partida.sav”. Dins seu es desa el número de combat actual i estat dels atributs del jugador. Immediatament, la partida acaba (sense comprovar si hi ha una màxima puntuació).

Cada cop que s’inicia un joc, es comprova si existeix aquest fitxer. Si no existeix, la partida s’inicia normalment com fins ara. Si existeix, el jugador i el número de combat s’obté d’aquest fitxer, de manera que la partida segueix exactament des del punt on es va desar. En fer-ho, el fitxer s’elimina, de manera que només és possible recuperar la partida una única vegada.

Porteu a terme aquesta tasca aplicant els principis bàsics de modularitat.

Classe DesarPartida.java. Nova classe que gestiona el fitxer on es desa la partida.

  1. package joc.arena.fitxers;
  2.  
  3. import java.io.File;
  4. import java.io.RandomAccessFile;
  5. import joc.arena.regles.Bestiari;
  6.  
  7. public class DesarPartida {
  8.  
  9. public static final File fitxer = new File("Partida.sav");
  10.  
  11. private Bestiari bestiari = new Bestiari();
  12.  
  13. /** Obté el número de combat de la partida desada.
  14.   *
  15.   * @return Número de combat
  16.   */
  17. public int obtenirNumCombat() {
  18. try {
  19. int num = 0;
  20. RandomAccessFile raf = new RandomAccessFile(fitxer,"r");
  21. num = raf.readInt();
  22. raf.close();
  23. return num;
  24. } catch (Exception e) {
  25. return 0;
  26. }
  27. }
  28.  
  29. /** Genera un jugador, mirant abans si hi ha una partida desada . Un cop
  30.   * recuperada la partida, s'esborra el fitxer automàticament.
  31.   *
  32.   * @return Jugador preparat per començar a jugar.
  33.   */
  34. public int[] obtenirJugador() {
  35. try {
  36. if (fitxer.isFile()) {
  37. int[] jugador = new int[9];
  38. RandomAccessFile raf = new RandomAccessFile(fitxer,"r");
  39. raf.skipBytes(4);
  40. for (int i = 1; i < jugador.length; i++) {
  41. jugador[i] = raf.readInt();
  42. }
  43. raf.close();
  44. fitxer.delete();
  45. return jugador;
  46. } else {
  47. return bestiari.generarJugador();
  48. }
  49. } catch (Exception e) {
  50. fitxer.delete();
  51. return bestiari.generarJugador();
  52. }
  53. }
  54.  
  55. /** Desa l'estat del jugador
  56.   *
  57.   * @param jugador Estat actual del jugador a desar.
  58.   * @return Si tot ha anat correctament (0) o no (-1).
  59.   */
  60. public int desarJugador(int[] jugador, int numCombat) {
  61. try {
  62. fitxer.delete();
  63. RandomAccessFile raf = new RandomAccessFile(fitxer,"rw");
  64. raf.writeInt(numCombat);
  65. for (int i = 1; i < jugador.length; i++) {
  66. raf.writeInt(jugador[i]);
  67. }
  68. raf.close();
  69. return 0;
  70. } catch(Exception e) {
  71. fitxer.delete();
  72. return -1;
  73. }
  74. }
  75.  
  76. }

Classe JocArena.java. Es modifica principalment el mètode inici.

  1. import joc.arena.fitxers.DesarPartida;
  2.  
  3. //...
  4.  
  5. private DesarPartida desar = new DesarPartida();
  6.  
  7. //...
  8.  
  9. public void inici() {
  10.  
  11. //Mirar si existeix fitxer d'adversaris
  12.  
  13. if (bestiari.existeixFitxer() == false) {
  14. System.out.println("No hi ha el fitxer d'adversaris!");
  15. } else {
  16. //CODI ORIGINAL
  17. sortida.mostarBenvinguda();
  18.  
  19. boolean jugar = true;
  20. boolean desada = false;
  21.  
  22. //Desar partida
  23. int numCombat = desar.obtenirNumCombat();
  24. int[] jugador = desar.obtenirJugador();
  25.  
  26. while (jugar) {
  27. numCombat++;
  28.  
  29. //Abans de cada combat es restaura al jugador
  30. lluitador.restaurar(jugador);
  31.  
  32. //Inici d'un combat
  33. System.out.println("*** COMBAT " + numCombat);
  34. System.out.print("Estat actual del jugador: ");
  35. sortida.mostrarLluitador(jugador);
  36. System.out.println("**************************");
  37.  
  38. //S'obte l'adversari
  39. int[] adversari = entrada.triarAdversari(lluitador.llegirNivell(jugador));
  40.  
  41. //Desar partida
  42. if (adversari == null) {
  43. int err = desar.desarJugador(jugador, numCombat - 1);
  44. if (err == 0) {
  45. jugar = false;
  46. desada = true;
  47. }
  48. } else {
  49.  
  50. //Combat
  51. combatre(jugador, adversari);
  52.  
  53. //Fi
  54. jugar = fiCombat(jugador, adversari);
  55. if (numCombat == MAX_COMBAT) {
  56. System.out.println("Has sobreviscut a tots els combats. Enhorabona!!");
  57. }
  58. }
  59.  
  60. }
  61.  
  62. if (desada) {
  63. System.out.println("Partida desada...");
  64. } else {
  65. System.out.print("Estat final del jugador: ");
  66. sortida.mostrarLluitador(jugador);
  67.  
  68. Ranquing rnk = new Ranquing();
  69. int punts = lluitador.llegirPunts(jugador);
  70. int pos = rnk.cercarRanking(punts);
  71. if (pos != -1) {
  72. if (pos < 10) {
  73. String inicials = entrada.preguntarInicials();
  74. int err = rnk.entrarPuntuacio(inicials, punts, pos);
  75. if (err == -1) {
  76. System.out.println("Error accedint al fitxer de puntuacions.");
  77. }
  78. }
  79. sortida.mostrarRanking();
  80. } else {
  81. System.out.println("Error accedint al fitxer de puntuacions.");
  82. }
  83. }
  84. }
  85. }

Classe EntradaTeclat.java. Ha de controlar si es vol desar en preguntar l’adversari.

  1. /** Tria l'adversari del jugador segons la seva resposta.
  2.   *
  3.   * @return Cadena de text amb la resposta, o null si es vol desar
  4.   */
  5. public int[] triarAdversari(int nivell) {
  6. System.out.print("Contra quin adversari vols lluitar en aquest combat? ");
  7. Scanner lector = new Scanner(System.in);
  8. String resposta = lector.nextLine();
  9. if ("desar".equalsIgnoreCase(resposta)) {
  10. return null;
  11. } else {
  12. int[] adversari = bestiari.cercarAdversari(resposta);
  13. if (adversari == null) {
  14. System.out.println("Aquest enemic no existeix. Es tria a l'aztar.");
  15. adversari = bestiari.triarAdversariAtzar(nivell);
  16. }
  17. return adversari;
  18.  

Anar a la pàgina següent:
Exercicis d'autoavaluació