1   package hirsipuu;
2   
3   import java.io.BufferedReader;
4   import java.io.File;
5   import java.io.FileNotFoundException;
6   import java.io.FileOutputStream;
7   import java.io.FileReader;
8   import java.io.IOException;
9   import java.io.PrintStream;
10  
11  
12  /**
13   * Luokka pisteiden k??sittelyyn. Osaa lukea Pisteet.dat tiedostoa ja palauttaa tasoa ja pelaajaa vastaavan pistem????r??n.
14   * @author Anna-Leena Latvala, Toni Purontaka
15   * @version v0.6, 15.4.2008
16   *
17   */
18  public class Pisteet {
19  
20      private final int tasojenlkm;
21      private PelaajanPiste[] pisteet = new PelaajanPiste[30];
22      private int lkm = 0;                                                // k?yt?ss? olevien pisteiden lkm
23      private final static String POLKU = "pisteet.dat";
24          
25      
26      /**
27       * Alustaa Pisteet defaulttitiedostosta.
28       */
29      public Pisteet(int lkm) {
30          tasojenlkm = lkm;
31          pisteetTiedostosta(POLKU);
32      }
33      
34      /**
35       * Alustaa Pisteet tiedostosta polussa "polku".
36       */
37      public Pisteet(String polku, int lkm) {
38          tasojenlkm = lkm;
39          pisteetTiedostosta(polku);
40      }
41      
42      /**
43       * Lukee tiedostosta pelaajan pisteet annettua tasoa vastaavasti. Tiedosto kirjoitetaan muotoon
44       * pid | tid | pisteet
45       * ja ;-alkuiset rivit tulkitaan kommenttiriveiksi.
46       * 
47       * @example
48       * <pre name="test">
49       * #THROWS IOException
50       * #import java.io.IOException;
51       * #import fi.jyu.mit.ohj2.VertaaTiedosto;
52       *  VertaaTiedosto.kirjoitaTiedosto("testitasoja.txt",
53       *      "1 | 1 | 12\n"+
54       *      ";tama rivi on kommentti\n" +
55       *      "1 | 2 | 100\n"+
56       *      "2 | 1 | 95");
57       *  Pisteet Pisteet = new Pisteet("testitasoja.txt", 2);
58       *  VertaaTiedosto.tuhoaTiedosto("testitasoja.txt");
59       * </pre>
60       *
61       */
62      public final void pisteetTiedostosta(String polku) {
63          BufferedReader fi;
64          
65          try {  //aukaistaan lukija
66            fi = new BufferedReader(new FileReader(polku));
67          } catch (FileNotFoundException ex) {
68            System.err.println("Tiedosto " + polku + " ei aukea!");
69            return;
70          }        
71                 
72          try {
73              String s;
74              while ( ( s = fi.readLine() ) != null) {
75                    lisaaPiste(s);
76               }
77              } catch (IOException ex) {
78            System.err.println("Virhe tiedostoa luettaessa!");
79            
80          } finally {  // suljetaan tietovirta
81            try {
82              fi.close(); 
83            } catch (IOException ex) {
84              System.err.println("Tiedostoa ei saa suljettua!");
85            }
86          }
87        }
88      
89      /**
90       * Tulostaa tasokohtaiset pisteet defaulttitiedostoon.
91       * @return true, jos tallennus onnistui, false muutoin
92       */
93      public boolean tallennaTiedot() {
94          return tallennaTiedot(POLKU);
95      }
96      
97      
98      /**
99       * Tulostaa pelaajien tasokohtaiset pisteet tiedostoon polussa "polku" ja muuttaa entisen siell? sijainneen
100      * tiedoston back-up tiedostoksi polku.bak (esim. pisteet.dat -> pisteet.dat.bak)
101      * @param polku tiedosto, johon tiedot kirjoitetaan
102      * @return true, jos tallennus onnistui, false muuten
103      * 
104      * @example
105      * <pre name="test">
106      * #THROWS IOException
107      * #import java.io.IOException;
108      * #import fi.jyu.mit.ohj2.VertaaTiedosto;
109      *  VertaaTiedosto.kirjoitaTiedosto("testipisteita.txt",
110      *      "; pid | tid | pisteet |\n" +
111      *      "1|1|12\n"+
112      *      "1|2|100\n"+
113      *      "2|1|95");
114      *      
115      *  Pisteet pis = new Pisteet("testipisteita.txt",3);
116      *  pis.tallennaTiedot("testitallennus.txt");           // ei ole testitallennus.txt ennen joten ei backuppia
117      *  
118      *  Pisteet pus = new Pisteet("testitallennus.txt",3);
119      *  pus.paivitaPiste(1,1,50);                           // muutetaan tietoja
120      *  pus.tallennaTiedot("testitallennus.txt");           // syntyy backup-tiedosto
121      *  
122      *  VertaaTiedosto.vertaaFileFile("testipisteita.txt", "testitallennus.txt.bak") === null;          // samat
123      *  (VertaaTiedosto.vertaaFileFile("testipisteita.txt", "testitallennus.txt") == null) === false;   // erit
124      *   
125      * VertaaTiedosto.tuhoaTiedosto("testipisteita.txt"); 
126      * VertaaTiedosto.tuhoaTiedosto("testitallennus.txt");          // tuhotaan syntyneet tiedostot
127      * VertaaTiedosto.tuhoaTiedosto("testitallennus.txt.bak");      // my?s back-up
128      * </pre>
129      */
130     public boolean tallennaTiedot(String polku) {
131         PrintStream fo;
132         File vanha = new File(polku);
133         File backuppi = new File(polku + ".bak");
134         vanha.renameTo(backuppi);
135         
136         try { 
137             fo = new PrintStream(new FileOutputStream(polku));
138         }  catch (FileNotFoundException ex) {
139             System.err.println("Tiedosto " + polku + " ei aukea!"); return false;
140         }
141 
142         try {
143             fo.println("; pid | tid | pisteet |");
144             for (int i = 0; i < lkm; i++) {
145                 fo.println(pisteet[i].tiedotToString());
146             }
147         } finally {
148             fo.close(); 
149         }
150         return true;
151         
152     }
153     
154     /**
155      * Palauttaa pelaajan kaikki pisteet kokonaislukutaulukkona.
156      * @param pelaajanid pyydetyn pelaajan id-numero
157      * @return 2d-kokonaislukutaulu, jossa vierekk?isiss? alkioissa on tason nro ja silt? saadut pisteet
158      *         tai 0 jos tasoa ei ole viel? pelattu.
159      * 
160      * @example
161      * <pre name="test">
162      * #THROWS IOException
163      * #import java.io.IOException;
164      * #import fi.jyu.mit.ohj2.VertaaTiedosto;
165      *  VertaaTiedosto.kirjoitaTiedosto("testitasoja.txt",
166      *      ";tiedostossa on pelaajien pisteit?\n" +
167      *      "1 | 1 | 12\n"+
168      *      "1 | 2 | 100\n"+
169      *      "2 | 1 | 95");
170      *  Pisteet pisteet = new Pisteet("testitasoja.txt", 8);
171      *  
172      *  int[][] taulu = pisteet.annaPisteet(1); //pelaajan 1 pisteet
173      *  taulu[0][0] === 1;
174      *  taulu[1][0] === 12;
175      *  taulu[1][1] === 100;
176      *  taulu[0][7] === 8;  // taso nro 8 on paikassa 7
177      *  taulu[1][4] === 0;  // tasolle 4 ei ole pisteit? joten pisteet on 0
178      *  
179      *  VertaaTiedosto.tuhoaTiedosto("testitasoja.txt");
180      * </pre>
181      */
182     public int[][] annaPisteet(int pelaajanid) {
183         int[][] palautus = new int[2][tasojenlkm];
184         for (int i=0; i < tasojenlkm; i++) {
185             palautus[0][i] = i+1;
186             for (int j=0; j < lkm; j++) {
187                 if (pisteet[j].pisteet(pelaajanid, i+1) > -1) {
188                     palautus[1][i] = pisteet[j].pisteet(pelaajanid, i+1);
189                     break;
190                 }
191             }
192         }
193         return palautus;
194     }
195     
196     /**
197      * P?ivitt?? pelaajan tasolta saamat pisteet. Jos ennen on saatu samalta tasolta paremmat
198      * pisteet, ei muuteta. Jos tehtiin uusi enn?tys, muutetaan, samoin kuin jos on kyseess? 
199      * 1. kerta kun tasoa pelataan.
200      * 
201      * @param pelaajanid tasoa pelanneen pelaajan id
202      * @param taso pelattu taso
203      * @param pisteet saadut pisteet
204      * @return true, jos saatiin paremmat pisteet kuin ennen
205      *         false, jos entiset pisteet olivat paremmat
206      *         
207      * @example
208      * <pre name="test">
209      * #THROWS IOException
210      * #import java.io.IOException;
211      * #import fi.jyu.mit.ohj2.VertaaTiedosto;
212      *  VertaaTiedosto.kirjoitaTiedosto("testitasoja.txt",
213      *      ";tiedostossa on pelaajien pisteit?\n" +
214      *      "1 | 1 | 12\n"+
215      *      "1 | 2 | 100\n"+
216      *      "2 | 1 | 95");
217      *  Pisteet pisteet = new Pisteet("testitasoja.txt", 8);
218      *  
219      *  pisteet.paivitaPiste(1,1,10) === false;
220      *  pisteet.paivitaPiste(1,2,120) === true;
221      *  pisteet.paivitaPiste(1,3,110) === true; // aivan uusi taso!
222      *   
223      *  int[][] taulu = pisteet.annaPisteet(1); 
224      *  taulu[1][0] === 12; // pelaaja 1 saa tasolta 1 yh? 12 pistett?.
225      *  taulu[1][1] === 120; // pelaaja 1 sai nyt tasolta 2 uudet 120 pistett?.
226      *  taulu[1][2] === 110; // pelaaja 1 sai nyt tasolta 3 pisteit?.
227      *  
228      *  VertaaTiedosto.tuhoaTiedosto("testitasoja.txt");
229      * </pre>
230      */
231     public boolean paivitaPiste(int pelaajanid, int taso, int pisteet) {
232         int vanhatpisteet;
233         
234         //tutkitaan, onko kyseess? vanha piste ja jos on, niin t?ydennet??n:
235         for (int i = 0; i<lkm; i++) {
236             vanhatpisteet = this.pisteet[i].pisteet(pelaajanid, taso);
237             if (vanhatpisteet > -1) {
238                 if (vanhatpisteet >= pisteet) return false;
239                 this.pisteet[i].setPisteet(pisteet);
240                 return true;
241             }            
242         }
243         
244         // jos p??st??n t?h?n niin kyseess? on uusi piste!
245         PelaajanPiste pp = new PelaajanPiste(pelaajanid, taso, pisteet);
246         lisaaPiste(pp.tiedotToString());
247         return true;
248         
249     }
250     
251     /**
252      * Jos annettu rivi s ei ole kommentti, siit? generoidaan PelaajanPiste-olio ja lis?t??n se
253      * tietorakenteeseen.
254      * @param s parsittava rivi
255      * @param i pelaajan pisteen j?rjestysnumero tietorakenteessa
256      */
257     private void lisaaPiste(String s) {
258         if ( s.charAt(0) != ';') {
259             PelaajanPiste pp = new PelaajanPiste();
260             pp.parse(s);
261             if (lkm >= pisteet.length) kasvata();
262             pisteet[lkm] = pp; 
263             lkm++;
264         }
265     }
266     
267     /**
268      * Kasvattaa pistetaulukon kokoa tarvittaessa kaksinkertaiseksi.
269      *
270      */
271     private void kasvata() {
272         PelaajanPiste[] uusi = new PelaajanPiste[2*pisteet.length];
273         int i = 0;
274         for (PelaajanPiste pp: pisteet) {
275             uusi[i] = pp;
276         }
277     }
278 }