Erinevus lehekülje "ITI0011:HW02 Droptris Juhend" redaktsioonide vahel
(ei näidata sama kasutaja 5 vahepealset redaktsiooni) | |||
26. rida: | 26. rida: | ||
Socket ühenduse realiseerimiseks on soovitatav kasutada järgmisi klasse: | Socket ühenduse realiseerimiseks on soovitatav kasutada järgmisi klasse: | ||
− | * <code>PrintWriter</code> andmete kirjutamiseks. | + | * <code>PrintWriter</code> andmete kirjutamiseks. Konstruktoris määrake teiseks argumendiks true (autoFlush), sellisel juhul iga <code>println</code> saadetakse kohe serverisse (muul juhul puhverdatakse saadetud sõnumid ja server saaks info kätte alles peale puhvri täitumist). |
* <code>InputStreamReader</code> andmete lugemiseks. | * <code>InputStreamReader</code> andmete lugemiseks. | ||
32. rida: | 32. rida: | ||
Andmeid tuleb lugeda sümbolhaaval. Kuna server ei saada reavahetusi, ei saa mõne muu lugejaga (nt BufferedReader) toimetada. Kuidas toimida: | Andmeid tuleb lugeda sümbolhaaval. Kuna server ei saada reavahetusi, ei saa mõne muu lugejaga (nt BufferedReader) toimetada. Kuidas toimida: | ||
− | * InputStreamReader'il on meetod <code>read(char[], int, int)</code>: https://docs.oracle.com/javase/7/docs/api/java/io/InputStreamReader.html#read%28char | + | * InputStreamReader'il on meetod <code>read(char[], int, int)</code>: [https://docs.oracle.com/javase/7/docs/api/java/io/InputStreamReader.html#read%28char%5B%5D,%20int,%20int%29 Java doc InputStreamReader#read] |
* Looge enda <code>char</code> massiiv, näiteks <code>char[] input = new char[100];</code> | * Looge enda <code>char</code> massiiv, näiteks <code>char[] input = new char[100];</code> | ||
* Andke loodud massiiv <code>read</code> meetodile ette. Samuti määrake ära algus ja lõpp. Te ei pea täpselt pihta saama lõpule. Pigem lugege natuke rohkem (kuna server on jõudnud vaid ühe kujundi info saata, ei tohiks seal ka mingit muud infot tulla). | * Andke loodud massiiv <code>read</code> meetodile ette. Samuti määrake ära algus ja lõpp. Te ei pea täpselt pihta saama lõpule. Pigem lugege natuke rohkem (kuna server on jõudnud vaid ühe kujundi info saata, ei tohiks seal ka mingit muud infot tulla). | ||
62. rida: | 62. rida: | ||
Kujundid genereeritakse juhuslikult. Levelid 1 - 7 genereerivad kujundeid ühtlase jaotusega. Levelid 8 ja 9 genereerivad "ebamugavamaid" kujundeid natuke rohkem. | Kujundid genereeritakse juhuslikult. Levelid 1 - 7 genereerivad kujundeid ühtlase jaotusega. Levelid 8 ja 9 genereerivad "ebamugavamaid" kujundeid natuke rohkem. | ||
+ | |||
+ | Ridade eemaldamise eest saab punkte: | ||
+ | * 1 rida 100 punkti | ||
+ | * 2 rida 200 punkti | ||
+ | * 3 rida 900 punkti | ||
+ | * 4 rida 1600 punkti | ||
=== Ühenduse loomine === | === Ühenduse loomine === | ||
86. rida: | 92. rida: | ||
** 7 - "O", "I", "J", "L", "T", "S", "Z" (kõik kujundid) | ** 7 - "O", "I", "J", "L", "T", "S", "Z" (kõik kujundid) | ||
** 8 - kõik kujundid, "O" ja "I" sagedus väiksem | ** 8 - kõik kujundid, "O" ja "I" sagedus väiksem | ||
− | ** | + | ** 9 - kõik kujundid, "O" ja "I" sagedus veel väiksem |
* lookahead - mitut järgmist kujundit ette näidatakse. Ettevaatamisega on võimalik paremat/täpsemat algoritmi teha. | * lookahead - mitut järgmist kujundit ette näidatakse. Ettevaatamisega on võimalik paremat/täpsemat algoritmi teha. | ||
* limit - mängitakse kuni selle punktisummani. See on mõistlik määrata näiteks põhiosas ette. | * limit - mängitakse kuni selle punktisummani. See on mõistlik määrata näiteks põhiosas ette. | ||
117. rida: | 123. rida: | ||
Vastus: | Vastus: | ||
<pre> | <pre> | ||
− | + | {"parameter": "score", "value": 400} | |
</pre> | </pre> | ||
168. rida: | 174. rida: | ||
'''Tähelepanu''': kui teie programm mõtleb liiga kaua, võib juhtuda, et serve on teile saatnud juba uue kujundi. Kui teie saadate serverisse käigutegemise päringu, paigutatakse see kujund, mis serveri jaoks on viimane (mitte see, mis teie programmi jaoks on viimane). Seega oluline on mitte väga kaua mõelda käigutegemise juures. | '''Tähelepanu''': kui teie programm mõtleb liiga kaua, võib juhtuda, et serve on teile saatnud juba uue kujundi. Kui teie saadate serverisse käigutegemise päringu, paigutatakse see kujund, mis serveri jaoks on viimane (mitte see, mis teie programmi jaoks on viimane). Seega oluline on mitte väga kaua mõelda käigutegemise juures. | ||
+ | |||
+ | == Näpunäiteid == | ||
+ | |||
+ | === Serverist saadetud sõnumid lähevad segamini === | ||
+ | |||
+ | Kui te mõtlete serveriga suhtluse peale, siis ajalises järjestuses võib see välja näha järgmiselt: | ||
+ | |||
+ | <pre> | ||
+ | teie programm server | ||
+ | |||
+ | ----------- ühendus --> | ||
+ | <-------- welcome ----- | ||
+ | |||
+ | <-------- block info -- | ||
+ | |||
+ | <-------- block info -- | ||
+ | |||
+ | --------- get score --> | ||
+ | <-------- score info -- | ||
+ | |||
+ | <-------- block info -- | ||
+ | </pre> | ||
+ | Nool tähistab, mis suunas teade liigub. Server saadab kujundiinfot. Teie teete korra punktisumma päringu ja saate ilusti vastuse. Kuna kujundiinfo saadetakse serverist eraldi lõimest (''thread''), ei tea tema midagi sellest, kas me oleme punktisummat või muud päringut koostanud. Mõelge järgmise ajajoone peale: | ||
+ | |||
+ | <pre> | ||
+ | teie programm server | ||
+ | |||
+ | ----------- ühendus --> | ||
+ | <-------- welcome ----- | ||
+ | |||
+ | <-------- block info -- | ||
+ | |||
+ | --------- get score --> | ||
+ | <-------- block info -- | ||
+ | <-------- score info -- | ||
+ | |||
+ | <-------- block info -- | ||
+ | </pre> | ||
+ | Ehk siis teie tegite punktisumma päringu, aga serveri kujundisaatmise protsess jõudis ette ja saatis kujundi info enne punktisumma vastust. | ||
+ | |||
+ | Teoreetiliselt võib juhtuda veel olukord, kus block ja score vastused on kokku pandud. | ||
+ | |||
+ | Kuidas sellises olukorras käituda? | ||
+ | |||
+ | Järgnevalt on välja toodud üks võimalik lahendus pseudokoodina | ||
+ | |||
+ | <pre> | ||
+ | |||
+ | function readAll() | ||
+ | read everything from the socket -> data | ||
+ | split data into separate inputs -> inputs[] | ||
+ | for each input in inputs: | ||
+ | check the contents of the input to detect input type | ||
+ | store every input type in a separate variable | ||
+ | input -> Someplace.message[type] | ||
+ | |||
+ | function readBlockMessage() | ||
+ | if Someplace.message["block"] exists | ||
+ | unset Someplace.message["block"] | ||
+ | return this message | ||
+ | else | ||
+ | readAll() | ||
+ | repeat this function | ||
+ | |||
+ | function readScoreMessage() | ||
+ | the same as readBlockMessage but with different message key. | ||
+ | |||
+ | main | ||
+ | while gameOn | ||
+ | blockMessage = readBlockMessage() | ||
+ | scoreMessage = readScoreMessage() | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | Ehk siis igakord, kui midagi loetakse, pannakse loetud info "puhvrisse". Kui puhvris juba on vajalik info, ei hakata uut päringut tegema. See on see näide, kui üritatakse punktisumma vastust lugeda, aga tuleb hoopis kujundiinfo. Kuna punktisumma vastust esimese vastusega ei tulnud, tehakse uus päring. Aga algselt saadud kujundiinfo päring salvestatakse ka puhvrisse. Järgmise päringuga saadakse punktisumma vastus kätte. Kui järgnevalt tehakse kujundi päring, on see info puhvris olemas ja päringut socketist pole vaja teha. |
Viimane redaktsioon: 22. märts 2016, kell 07:52
Üldine
Siin on mõned näpunäited, mida Droptrise ülesande juures võiks arvess võtta.
Nõuded
Põhiosa
Põhiosa (4p) jaoks tuleb mängida nii "O" kui "I" kujunditega vähemalt 2000 punkti.
Võimalik on saada ka 2p (+1 palli siis ei rakendu) kui vaid "O" kujunditega saab 2000 punkti.
Lisaosa
Põhiosa on võimalik realiseerida kasutades meie etteantud teeki DroptrisConnection. Kõik lisaosad eeldavad, et realiseerida socket ühenduse ise.
Ülesande lisaosade jaoks peab teie algoritm suutma mängida kõikide kujunditega.
Lisaosade eest on võimalik saada täiendavalt 4p. Täpsed kriteeriumid selguvad.
Võistlus
Täiendavalt on võimalik saada kuni 2 lisapunkti. Teie realiseeritud algoritmid pannakse mängima samade klotside järjestustega. Parimad saavad lisapunkte.
Socket ühendus
Socket ühenduse realiseerimiseks on soovitatav kasutada järgmisi klasse:
PrintWriter
andmete kirjutamiseks. Konstruktoris määrake teiseks argumendiks true (autoFlush), sellisel juhul igaprintln
saadetakse kohe serverisse (muul juhul puhverdatakse saadetud sõnumid ja server saaks info kätte alles peale puhvri täitumist).InputStreamReader
andmete lugemiseks.
Kasutades PrintWriter
klassi, saate kasutada println
meetodit, et oma päringud saata. Oluline on just saata ka reavahetus (server eeldab seda).
Andmeid tuleb lugeda sümbolhaaval. Kuna server ei saada reavahetusi, ei saa mõne muu lugejaga (nt BufferedReader) toimetada. Kuidas toimida:
- InputStreamReader'il on meetod
read(char[], int, int)
: Java doc InputStreamReader#read - Looge enda
char
massiiv, näitekschar[] input = new char[100];
- Andke loodud massiiv
read
meetodile ette. Samuti määrake ära algus ja lõpp. Te ei pea täpselt pihta saama lõpule. Pigem lugege natuke rohkem (kuna server on jõudnud vaid ühe kujundi info saata, ei tohiks seal ka mingit muud infot tulla). - Sisend pannakse char massiivi, selle saate
String
objektiks muuta (näiteksString
konstruktoriga).
Mõistlik oleks lugemise jaoks teha mingi eraldi meetod. Ja veel vingem oleks teha kogu socket ühenduse jaoks eraldi klass, millel on read/write vms meetodid. Näiteks võiks teie loodud klassi kasutamine välja näha selliselt:
<source lang="java"> UltimateDroptrisConnection c = new UltimateDroptrisConnection(jsonString); c.read(); // reads welcome message BlockInformation bi = c.readBlockInformation(); char block = bi.getBlock(); char[] nextBlocks = bi.getNextBlocks();
int score = c.readScore();
int[][] state = c.readState(); </source>
Eelnev on lihtsalt näide. Te ei pea üldse oma klassi looma ühenduse jaoks.
API kirjeldus
Mängu serveriga tuleb luua socket ühendus.
Server ei lõpeta ühendust ära. Samuti ei anna server otseselt märku, kui mäng on läbi saanud. Mõistlik oleks määrata ühendusele timeout. Kui timeout tuleb, siis arvatavasti on mäng läbi.
Mäng toimub 20 (kõrgus) x 10 (laius) "laual".
Kujundid genereeritakse juhuslikult. Levelid 1 - 7 genereerivad kujundeid ühtlase jaotusega. Levelid 8 ja 9 genereerivad "ebamugavamaid" kujundeid natuke rohkem.
Ridade eemaldamise eest saab punkte:
- 1 rida 100 punkti
- 2 rida 200 punkti
- 3 rida 900 punkti
- 4 rida 1600 punkti
Ühenduse loomine
{ "uniid": "mati.gaal", "seed": 12345678, "level": 1, "lookahead": 0, "limit": 2000 }
- uniid - teie uniid
- seed - kasutatakse juhuarvude initsialiseerimisel. Kasutades sama seed väärtust saate oma koodi samade kujunditega testida. Hiljem ülesande testimisel kasutatakse suvalisi seed väärtusi.
- level - level, millega mängida
- 1 - ainult "O" kujundid
- 2 - "O" ja "I"
- 3 - "O", "I", "J"
- 4 - "O", "I", "J", "L"
- 5 - "O", "I", "J", "L", "T"
- 6 - "O", "I", "J", "L", "T", "S"
- 7 - "O", "I", "J", "L", "T", "S", "Z" (kõik kujundid)
- 8 - kõik kujundid, "O" ja "I" sagedus väiksem
- 9 - kõik kujundid, "O" ja "I" sagedus veel väiksem
- lookahead - mitut järgmist kujundit ette näidatakse. Ettevaatamisega on võimalik paremat/täpsemat algoritmi teha.
- limit - mängitakse kuni selle punktisummani. See on mõistlik määrata näiteks põhiosas ette.
Server vastab teatega ühenduse õnnestumise/ebaõnnestumise kohta (teade võib natuke erineda).
{"message": "Game is starting! Good luck! Playing to 2000 points!", "code": 200}
Kujundiinfo
Kujundiinfo jaoks ei ole teil vaja päringut teha - server saadab teatud intervalliga (0.25s - 0.5s) seda ise. Seega peate te pidevalt lugema infot.
{"block": "L", "next": ["I"]}
- block - mis on praegune kujund
- next - massiiv järgmistes kujunditest nende ilmumise järjekorras.
Punktisumma
Punktisummat võite ka enda poolel hoida, kuid mõistlik on usaldada serveri infot.
Päring:
{"parameter": "score"}
Vastus:
{"parameter": "score", "value": 400}
- value - punktisumma
Mänguseis
Tagastab serveris oleva mängu seisu.
Päring:
{"parameter": "state"}
Vastus:
{"parameter": "state", "value": [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}
... asemel on veel 18 rea info. Ehk siis tulemus koosneb 20-st massiivist, iga massiivi sees on massiiv 10 elemendiga. Tekib 2-mõõtmeline "lauaseis". 0 tähistab tühja kohta, 0-st erinev arv täidetud kohta.
Mängu staatus
Päring:
{"parameter": "status"}
{"parameter": "status", "value": "playing"}
Võimalikud väärtused:
- playing - mäng käib
- game over - mäng läbi
- you win - võit (ettemääratud punktisumma on saavutatud).
Käigu tegemine
Käigu tegemiseks peate saatma serverile kujundi asukoha (veeru indeks) ja pöörde (vt. [ITI0011:HW02_Droptris#Kujundid]).
Päring:
{"column": 4, "rotation": 1}
Päringuga saate liigutada seda kujundit, mis server viimati saatis (mitte tingimata seesama, mis teie viimati vastu võtsite - võib juhtuda, et olete vahepeal lugemisega midagi vahele jätnud).
- column - veeru indeks (0 - 9). Sellesse veergu läheb kujundi kõige vasakpoolsem osa.
- rotation - vaata pöördeid kujundite kirjelduse juures.
Tähelepanu: kui teie programm mõtleb liiga kaua, võib juhtuda, et serve on teile saatnud juba uue kujundi. Kui teie saadate serverisse käigutegemise päringu, paigutatakse see kujund, mis serveri jaoks on viimane (mitte see, mis teie programmi jaoks on viimane). Seega oluline on mitte väga kaua mõelda käigutegemise juures.
Näpunäiteid
Serverist saadetud sõnumid lähevad segamini
Kui te mõtlete serveriga suhtluse peale, siis ajalises järjestuses võib see välja näha järgmiselt:
teie programm server ----------- ühendus --> <-------- welcome ----- <-------- block info -- <-------- block info -- --------- get score --> <-------- score info -- <-------- block info --
Nool tähistab, mis suunas teade liigub. Server saadab kujundiinfot. Teie teete korra punktisumma päringu ja saate ilusti vastuse. Kuna kujundiinfo saadetakse serverist eraldi lõimest (thread), ei tea tema midagi sellest, kas me oleme punktisummat või muud päringut koostanud. Mõelge järgmise ajajoone peale:
teie programm server ----------- ühendus --> <-------- welcome ----- <-------- block info -- --------- get score --> <-------- block info -- <-------- score info -- <-------- block info --
Ehk siis teie tegite punktisumma päringu, aga serveri kujundisaatmise protsess jõudis ette ja saatis kujundi info enne punktisumma vastust.
Teoreetiliselt võib juhtuda veel olukord, kus block ja score vastused on kokku pandud.
Kuidas sellises olukorras käituda?
Järgnevalt on välja toodud üks võimalik lahendus pseudokoodina
function readAll() read everything from the socket -> data split data into separate inputs -> inputs[] for each input in inputs: check the contents of the input to detect input type store every input type in a separate variable input -> Someplace.message[type] function readBlockMessage() if Someplace.message["block"] exists unset Someplace.message["block"] return this message else readAll() repeat this function function readScoreMessage() the same as readBlockMessage but with different message key. main while gameOn blockMessage = readBlockMessage() scoreMessage = readScoreMessage()
Ehk siis igakord, kui midagi loetakse, pannakse loetud info "puhvrisse". Kui puhvris juba on vajalik info, ei hakata uut päringut tegema. See on see näide, kui üritatakse punktisumma vastust lugeda, aga tuleb hoopis kujundiinfo. Kuna punktisumma vastust esimese vastusega ei tulnud, tehakse uus päring. Aga algselt saadud kujundiinfo päring salvestatakse ka puhvrisse. Järgmise päringuga saadakse punktisumma vastus kätte. Kui järgnevalt tehakse kujundi päring, on see info puhvris olemas ja päringut socketist pole vaja teha.