Ohjelmoinnin peruskurssi Y2, kurssimateriaali

4.4. Tehtävä: Reading a chunked file

«  4.3. Läpikäynnit, iteraattorit, generaattorit   ::   Etusivulle   ::   4.5. Tehtävä: Reading a human-writeable file  »

4.4. Tehtävä: Reading a chunked file

Pistearvo: 200p

Teema

Tämän ja seuraavan luvun tehtävissä luetaan eri formaatein tallennettua dataa. Molemmissa luettava data kuvaa shakkipelin tilanteita. Shakkipeliä kuvaavat luokat ja tiedoston tulkintaa varten olevan luokan pohja annetaan valmiina.

Tavoitteet

  • Harjoitellaan lukemista tietovirrasta.
  • Tutustutaan mahdolliseen tapaan säilyttää tietoa tiedostossa.
  • Kirjoitetaan isomman ohjelman keskeinen osa. Lähes jokaisessa projektiaiheessa ohjelman tulee pystyä lukemaan ja tallentamaan tiedostoja. Monessa työssä formaatti voi olla yksinkertaisempi.

Intro : Tiedostoformaateista

Yksi keskeinen ominaisuus lähes joka ohjelmassa on mahdollisuus tallentaa ohjelman toimintaan liittyvää tietoa tiedostoihin sekä ladata ohjelman käyttöön. Tallennettavalla tiedolla on lähes aina jokin selkeä rakenne. Tiedon esitystapaa tiedostoissa kutsutaan tiedostoformaatiksi.

Formaattien suunnitteluun tai olemassa olevan formaatin valintaan johonkin tehtävään vaikuttaa joukko erilaisia tavoitteita kuten

  • Luettavuus

  • Editoitavuus (käsin tai apuohjelmilla)
    • Kirjoittaminen alusta myös käsin
    • Mahdollisuus lisätä väliin
  • Virheherkkyys
    • Yhtälailla formaatin kuin parserin (tiedoston tulkitsijan) tehtävä
  • Tilankäyttö

  • Formaatin laajennettavuus

  • Nopeus

  • Tunnetut formaatit (esim CSV) joita käyttämällä ohjelman tallentama data on muilla ohjelmilla käsiteltävissä.

Lohkopohjainen formaatti

Tässä tehtävässä kirjoitetaan ohjelma, joka lukee lohkopohjaista formaattia. Mm. monet kuva- ja ääniformaatit ovat tällaisia. Niitä on helppo lukea ja kirjoittaa ohjelmilla. Nämä formaatit ovat helposti laajennettavissa: Niihin voidaan lisätä tietoa, jota kaikkien niitä lukevien ohjelmien ei tarvitse ymmärtää, mutta kaikki ohjelmat pystyvät silti käsittelemään olennaiset perustiedot. Mm. kameroiden tuottama lisäinformaatio kuvasta on voitu liittää olemassaoleviin kuvatyyppeihin muuttamatta tiedostojen alkuperäistä formaattia.

Esimerkkejä muista formaateista

Seuraavassa luvussa käsitellään ihmiselle helpommin käsiteltävää formaattia.

Tehtäväpohja : tehtävässä käytettävä formaatti

Tehtävän formaatti on kehitetty shakkipelin tilanteen tallennukseen. Tässä on pelkkä lopputilanne, ei siirtoja. Alla on esitetty esimerkkitallennus ja sen jälkeen seuraa formaalimpi esitys siitä millainen tehtävän käyttämä rakenne on.

Esitetyn formaatin suunnitteluperiaatteina on sen luettavuus helposti ohjelman toimesta, mahdollisuus lisätä uusia lohkoja säilyttäen silti mahdollisuus lukea tietoa vanhoilla ohjelmilla sekä hyvä tilankäyttö. Tiedostossa on myös versionumero ja tunniste, joiden avulla voidaan estää tilanteet, joissa yritetään lukea vääräntyyppistä dataa.

Esimerkki

Tiedostossa ei ole rivinvaihtoja eikä kaksoispisteitä! Kuvassa teksti on vain jaettu lyhyempiin pätkiin esittämisen helpottamiseksi.

Yllä oleva tekstinä

SHAKKI1205072001CMT54Laurin revanssipeli, hyvin huonosti on taas menossa...PLR17M5MarkoKa4Ta6b3c3PLR13V5LAURIKd3Rf1END00

Tiedoston rakenne

Katsele tätä osiota lukiessasi samalla yllä olevaa kuvaa.

Tiedosto koostuu otsikosta ja joukosta lohkoja. Tarkemmin ilmaistuna sen rakenne on seuraava:

Header : 8 merkkiä
   teksti "SHAKKI" : 6 merkkiä
   versionumero    : 2 merkkiä

Päiväys : 8 merkkiä
   DDMMYYYY : 8 merkkiä
   (DD on päivä väliltä 01-31, MM on kuukausi väliltä 01-12, YYYY on vuosi)

(1 - N) mitä tahansa lohkotyyppiä, kuitenkin niin että:
   * viimeinen on END-lohko
   * PLR lohkoja on tasan 2 kappaletta ja niillä on eri värit

Lohkot

Lohkojen rakenne on seuraava:

Tunniste     : 3 merkkiä (esim CMT, PLR, END)
datan pituus : 2 merkkiä (sisältää numeron)
data         : (datan pituus) merkkiä

Datan pituus kertoo lohkoon kuuluvien merkkien määrän poislukien tunnisteen ja datan-pituus-merkinnän itsensä viemät merkit. Toisin sanoen, numeron luettuaan voi huoletta lukea numeron kertoman määrän merkkejä.

Lohkojen kuvaukset

Kommenttilohko (tunniste CMT)

Kommenttilohko. Koko lohkon sisältö on merkkijono, joka kuvaa tallennettua tilannetta.

Pelaajalohko (tunniste PLR)

Pelaajalohko. Ehjässä tiedostossa näitä on aina kaksi kappaletta eri väreillä.

Pelaajan väri : 1 merkki (M tai V)
Nimen pituus  : 1 merkki (väliltä 1-9)
Nimi          : (nimen pituus) merkkiä
Pelaajan nappulat shakkinotaatiolla : lohkon loppuun asti

Huom: Tiedoston avulla voidaan esittää myös shakkitehtäviä, joissa nappuloita voi olla mikä tahansa määrä mitä tahansa nappulatyyppiä. Sinun ei siis tarvitse pohtia shakin nappuloiden määriä tai voiko johonkin tilanteeseen päästä.

Shakkinotaatio:

Shakkilaudan koko on 8x8. Laudalla on 8 riviä jotka on numeroitu 1-8, sekä 8 saraketta jotka on merkitty pienin kirjaimin a-h. Laudan nurkat ovat siis paikoissa a1, a8, h1 ja h8.

Shakissa on 6 erilaista nappulaa (suluissa tehtävässä käytetty suomalainen lyhenne, jota käytämme tallennuksessa):

  • Kuningas/King (K)
  • Kuningatar/Queen (D)
  • Torni/Rook (T)
  • Lähetti/Bishop (L)
  • Ratsu/Knight (R)
  • Sotilas/Pawn ().

Jos halutaan merkitä, että kuningas on ruudussa a3, kirjoitetaan "Ka3". Torni ruudussa h8 merkittäisiin "Th8". Sotilaalla ei ole notaatiossa merkkiä, joten sotilas ruudussa b7 merkittäisiin "b7".

Nappuloiden paikat on tiedostossa lueteltu peräkkäin ilman erotinmerkkejä. Jos ensimmäinen merkki on välillä a-h, niin kuvattu merkki on sotilas, muuten merkki kertoo nappulan tyypin.

Lopetuslohko (tunniste END)

Lopetuslohko. End-lohkon koko on aina 00, ja se lopettaa tiedoston, joten end-lohkon luettuaan tiedoston voi sulkea.

Muut lohkot (tuntematon tunniste *** sisältäen mitkä tahansa kolme merkkiä)

Tuntemattomat lohkot ovat todennäköisesti formaattiin uudemmissa versioissa lisättyjä ominaisuuksia. Näistä luetaan vain lohkon koko ja ohitetaan merkit skip-komennolla.

Tehtävänanto

Toteuta objektiin game.ChunkIO pyydetty metodi

  • def load_game(self, input)

    Luo uuden Game-olion, jonka se metodin päätteeksi palauttaa. Metodi lukee pelin pelaajat ja nappuloiden sijainnin käyttäen apuna muita tiedostossa määriteltyjä metodeja. Lopetuslohkon luettuaan palauttaa peliolion, jossa on tiedostossa annetut pelaajat ja lauta asetettuna. Kommenttilohko ja mahdolliset tuntemattomat lohkot metodin tulee ohittaa. Kun toivottavasti tesataat ohjelmaasi, luo ja sulje tietovirta testiluokassasi, ei tässä metodissa.

    Jos luettavassa tiedostossa on jotain pielessä, metodin tulee heittää poikkeus CorruptedChessFileException. Annettu tehtäväpohja heittää jo tällaisen poikkeuksen tilanteessa, jossa tietovirran luvussa on jokin ongelma. Sinun pitäisi heittää sellainen, jos luetun tiedon rakenteessa on jokin ongelma. Tällaisia ovat esim. puuttuva lopetuslohko, poikkeava määrä pelaajalohkoja, pelilaudalla päällekkäin olevat nappulat jne...

Valmiiksi annettu koodi

board.py. (Ei palauteta)
Hyvin yksinkertainen luokka joka kuvaa pelilautaa. Mahdollistaa nappuloiden sijoittamisen laudalle ja laudan ruutujen tarkastelun. Sisältää apumetodin sarakekirjainten muuntamiseen ruudukon indekseiksi.
player.py. (Ei palauteta)
Hyvin yksinkertainen luokka joka kuvaa pelaajaa. Mahdollistaa nimen ja pelaajan värin asettamisen ja näiden attribuuttien lukemisen.
game.py. (Ei palauteta)
Hyvin yksinkertainen luokka joka koostaa pelissä tarvitut luokat yhteen. Mahdollistaa pelaajien ja pelilaudan asettamisen ja hakemisen.
piece.py. (Ei palauteta)
Hyvin yksinkertainen luokka joka kuva shakkinappulaa. Shakkinappulalla on omistaja (pelaaja) sekä tyyppi (Kuningas, Ratsu, jne...)
corrupted_chess_file_error.py. (Ei palauteta)
Poikkeusluokka tiedostonluvussa esiintyvien ongelmien esittämiseen.
broken_reader.py. (Ei palauteta)
Apuluokka virhetilanteiden testaamiseen.
chunkIO.py. Palautetaan
Apuluokka jonka avulla voidaan ladata ja tallentaa(ei tässä harjoituksessa) pelitilanne tiedostosta. Toteuta luokkaan vaadittujen metodien lisäksi halutessasi myös apumetodeja. Esimerkiksi eri tyyppisten lohkojen lukemiseen voi olla kätevää tehdä omat metodit. test.py Palautetaan
test.py. Palautetaan
Testitiedoston pohja. Testiluokan nimen tulee olla Test jotta A+ tunnistaa sen. Eli Test tiedostossa test.py.

Testien kuvaukset

Tässä on kuvattu tehtävässä käytetty testidata. Voitte muokata tämän perusteella itsellenne testit, jotta pääsette debuggaamaan.

Given Example

SHAKKI1205072001CMT54Laurin revanssipeli, hyvin huonosti on taas menossa...PLR17M5MarkoKa4b3Ta6c3PLR13V5LauriKd3Rf1END00

Another Test

Pelaajien nimet ja kommenttilohkon sisältö ovat kummallisia, mutta herrojen PLR ja CMTEND pelissä ei ole mitään muuta kummallista.

SHAKKI1225072008CMT12PLRENDENDCMTPLR15M3PLRRh4e3Lb6c3PLR11V6CMTENDKa1END00

Unknown Chunks

Tehtävänannon mukaisesti tiedostossa saa olla tuntemattomia lohkoja kuten 'BAT101234567890' ja 'HUH00'. Nämä täytyy vain ohittaa ottamatta niihin kantaa.

SHAKKI1205072001BAT101234567890CMT54Laurin revanssipeli, hyvin huonosti on taas menossa...PLR17M5MarkoKa4b3Ta6c3HUH00PLR13V5LauriKd3Rf1END00

Broken file

Suurin osa testeistä menee läpi itsestään, vain nappuloiden nimet ja sijainti voivat olla väärin tai toisen pelaajan nappulat puuttua.

  • Testataan rikkinäinen tiedostoheader (menee läpi jo valmiilla tehtäväpohjalla)
  • Testataan tiedoston äkillinen päättyminen (Ei tarvitse tarkastella - menee läpi kunhan et kaappaa IO-poikkeuksia, vaan annat sen muuttua päämetodissa CorruptedChessFileExceptioniksi)
  • Testataan kuvitteelinen nappi 'Ha1'
  • Testataan kuvitteellinen sijainti 'x1'
  • Testataan toisen PLR-lohkon puuttuminen kokonaan
  • Testataan END:in puuttuminen (menee läpi tekemättä muutoksia kuten äkillinen päättyminen). Seuraavaa lohkoa luettaessa syntyy IOException -> CorruptedChessFileException.
  • Testataan lopuksi vielä että ehjä tiedosto menee myös läpi

Tyypillisimmät koodausvirheet

  • On oletettu jokin järjestys lohkoille (musta tai valkoinen ensin, CMT-lohko alussa tai lopussa jne.) Huomaa, että ainoastaan END-lohkon sijainti on speksattu.
  • Käytetään luettuja merkkejä suoraan indekseinä (ei muunneta kirjaimia '5', 'a' numeroiksi). Kts. Board-luokan apumetodit, getChunkSize
  • Unohdetaan lukea seuraava chunkHeader
  • Unohdetaan ohittaa tuntemattomat lohkot (luetaan vain header)
  • Unohdetaan luoda pelilauta tai asettaa se peliin.
  • Luodaan yksi pelaaja laudalle ja toinen samanniminen joka merkitään nappuloihin

Palautus

«  4.3. Läpikäynnit, iteraattorit, generaattorit   ::   Etusivulle   ::   4.5. Tehtävä: Reading a human-writeable file  »