Tikumängu ülesanne

Allikas: Lambda

Praktikumitöö tuleb teha vastavalt siin failis
antud täpsustustele. Kõigepealt seletan,
kuidas tikumäng käib, siis seda, kuidas
seal võita, ning seejärel annan
juhendid ja soovitused programmeerimiseks.


Kuidas tikumängu mängitakse
---------------------------

Vaatame kõigepealt hästi lihtsat
varianti, siis kirjeldame selle väikest
üldistust, st natuke keerulisemat varianti.

Lihtsas variandis on algseisus laual kümme tikku:

| | | | | | | | | |

Kaks mängijat võtavad vaheldumisi laualt tikke
ära. Kumbki võib võtta kas ühe, kaks või kolm
tikku. Võtmata jätta ei tohi ja üle kolme võtta
ei või. 

Kes võtab viimase tiku, on võitnud.

Näiteks võib mäng käia nii:

Mängija A võtab kaks tikku. Lauale jääb kaheksa:

| | | | | | | | 

Mängija B võtab kolm tikku. Lauale jääb viis:

| | | | |

Mängija A võtab ühe tiku. Lauale jääb neli:

| | | |

Mängija B võtab ühe tiku. Lauale jääb kolm:

| | | 

Mängija A võtab kõik kolm tikku, lauale ei
jää enam ühtegi ja mängija A on võitnud.


Keerukamas variandis on põhimõte sama, kuid:

- Laual võib algseisus olla suvaline arv tikke
  (st päris palju, kasvõi sada) 
  
- Maksimaalne võetav tikkude arv ei pruugi olla kolm,
  vaid võib olla suvaline number kahest alates.
  
Keerukamas mänguvariandis on seega vaja enne mängu
algust kokku leppida:

- Algseisus olevate tikkude arv N
- Maksimaalne võetavate tikkud arv M.

Näiteks, M võib olla kaks - siis tohib võtta
kas ühe või kaks tikku. Kui M on viis, tohib
võtta kas 1,2,3,4 või 5 tikku.

Mängu käigus loomulikult M-i ei muudeta,
M lepitakse kokku enne mängu algust.


Kuidas tikumängus võita
-----------------------

Üks viis tikumängus võitmiseks oleks
mõelda ette oma võimalikud käiguvariandid,
vastase võimalikud käiguvariandid, seejärel
tekkivad oma käiguvariandid jne jne, justnagu
males või kabes.

Õnneks on tikumängu võimalik võita ka palju
lihtsamini: nii, et üldse pole vaja midagi
ette mõelda!

Vaatame näiteks lihtsat tikumängu: 10 tikku
algseisus (N=10) ja maksimaalselt kolm võetavat
tikku (M=3). Selge, et kui vastasel on tema
käigukorra ajal ees neli tikku:
| | | |
siis on vastane kindlas kaotusseisus: mina
saan igal juhul viimase tiku. 

Kuidas teha nii, et vastasele jääks ette neli
tikku? Kui vastasel on ees kaheksa tikku:
| | | | | | | |
siis saan ma igal juhul tekidada olukorra,
kus peale tema käiku ja seejärel minu käiku
on temal ees neli tikku, ning seega saan
lõpuks võita.

Üldpõhimõte on järgmine: 

- mängija on kaotusseisus, kui
  jääk(N/(M+1))=0
  see tähendab: kui jagan laual olevate
  tikkude arvu arvuga M+1 ja võtan jäägi 
  (näiteks, 5/3 annab jäägi 2), siis seis
  on kaotusseis, kui jääk on 0.
  
- igal muul juhul on mängija võiduseisus ning
  võidukäigu saab ta arvutada sama põhimõtte
  järgi:
  võidukäik=jääk(N/(M+1))


Kuidas tikumängu praktikumis programmeerida
===========================================

Kõigepealt, sinu programm peab vastama järgmistele
nõuetele:

- Programm läheb käima käsurealt ja trükkimine ning
  käikude lugemine toimub samuti lihtsalt käsurea
  viisil, st mingeid eraldi aknaid programm ei tee.
  
- Programm suudab võiduseisust alati võita.
  
- Kaotusseisus teeb programm juhusliku reeglitega
  lubatud käigu (näiteks, lihtsas tikumängus valib
  programm nelja tikuga seisus käiguks juhuslikult
  kas 1, 2 või 3).
  
- Enne mängu algust peab programm kasutajalt küsima
  ja endale meelde jätma:
  - algseisus olevate tikkude arvu N
  - maksimaalse võetavate tikkude arvu M
  - mängu alustaja (masin või inimene)
  
- Programm peab ise mängima vastavalt reeglitele
  ja ei tohi lubada vastasel teha keelatud
  käike (vastane ei tohi saada võtta 0
  tikku või rohkem tikke, kui M). Kui vastane
  proovib teha keelatud käiku, siis programm teatab,
  et see pole lubatud käik ja küsib käiku uuesti.
  
- Programm peab inimmängijale arusaadavalt iga
  oma käigu juures trükkima välja 
  - kõigepealt, laual olevate tikkude arvu
  - seejärel oma käigu
  - seejärel lauale jäävate tikkude arv
  - seejärel küsima kas inimmängija käiku või
    teatama oma võidust.

- Programm peab aru saama, kui programm või
  inimmängija on võitnud ning selle info ilusti
  välja trükkima.

- Kui mäng saab läbi, siis peab programm küsima
  inimmängijalt, et kas too tahab veel mängida või
  ei. Kui mängija ei taha rohkem mängida, siis
  programm lõpetab töö.
  
- Programmi java koodis peab taande kasutamine
  olema mõistlik ja ilus, samuti peab seal olema
  mõistlikus koguses mõistlikke kommentaare!
  
  Ilma kommentaarideta ja/või nõmeda taandega
  programmi ei pruugi praktikumijuhendaja vastu võtta.
  

Järgnevalt mõned soovitused programmeerimiseks
==============================================
  
Alusta programmeerimist sellest, et kirjuta ise või
kopeeri endale HelloWorld programm, kompileeri
ja lase käima:

public class HelloWorld {
            
  // A program to display the message
  // "Hello World!" on standard output

  public static void main(String[] args) {
    System.out.println("Hello World!");
  }
                 
}   // end of class HelloWorld


Seejärel kopeeri see fail "päris" tikumängu
failiks nimega Tikumang.java ja asenda tema
sees klassi nimi "HelloWorld" nimega "Tikumang".
NB! Jutumärke ära muidugi pane, täpitähti ära
kasuta, ning arvesta, et faili nimi ja 
klassi nimi peavad olema samad (välja arvatud
.java lõpp) ja suured/väikesed tähed on erinevad.

Kompileeri see uus fail ka ära ja pane käima.
Nüüd hakka programmi kirjutama "main" funktsiooni
sisse, kus esialgu on ainult rida
System.out.println("Hello World!");
(selle rea võid ära ka kustutada).

Mingeid muid funktsioone pole vaja teha.
Programmi saab kokku panna lihtsalt if-dest,
tsüklitest, arvutustest, lugemisest ja
trükkimisest.




Kuidas trükkida:
----------------

Kasuta lihtsalt System.out.println
funktsiooni. Sellega saad trükkida nii
stringe kui arve.

Kui tahad, et trükitava lõpuks poleks
reavahet, kasuta hoopis
System.out.print funktsiooni (nime
lõpus puudub ln).

Kuidas lugeda inimmängija käike ja algseisu jne
-----------------------------------------------

Loe kõigepealt Ecki õpikust peatükk
2.4 (URL: http://math.hws.edu/javanotes/c2/s4.html)
Seal on õpetus, kuidas seda mugavalt teha.
Sinul läheb vaja funktsiooni TextIO.getInt(),
muid polegi otseselt vaja.

NB! Selleks, et kasutada TextIO.getInt()
funktsiooni, peab sinu arvutis sellesamas
kataloogis, kus on tikumängu programmi fail,
asuma Ecki kirjutatud programmifail
TextIO.java. Selle faili saad võrgust
tõmmata URL-ilt 
http://math.hws.edu/javanotes/source/TextIO.java
ning ta oma kataloogi kirjutada.

Proovi ta ära ka kompileerida. Käima see
TextIO programm ise ei lähe, sest tal pole
main funktsiooni sees.

Idee selles, et kui Java kompilaator/mootor näeb
funktsiooni TextIO.getInt(), siis ta asub
otsima faili TextIO, kust seda funktsiooni leida
võiks. Otsib ta faili kõigepealt sealtsamast kataloogist,
kus sinu programmifail asub.

NB! AK arvutiklassides kasuta nii kompilaatori javac kui 
interpretaatori java jaoks lisaparameetrit -cp .
(javac -cp . minuprogramm.java), muidu ei leita .class
faile sinu hetkekataloogist üles.

Kuidas hoida hetkeseisu, vastase tehtud käiku jne?
--------------------------------------------------

See on väga lihtne: iga selline info on lihtsalt
üks täisarv ja teda saab hoida ühes täisarvu
tüüpi muutujas.

Näiteks, mänguseisu saabki hoida muutujas nimega n.

Deklareeri programmi algul:

int n;

ja hiljem muuda seda n-i. Las n sisaldab
alati laual olevate tikkude arvu.

NB! Muutujate nimedes (nagu ka mujal) on väikesed
ja suured tähed erinevad asjad.

Mulle endale meeldib aga kasutada pikemaid
muutujate nimesid, sest siis saan muutujat
nähes alati aru, mis seal sees peaks olema.

Mina kasutaksin mänguseisu hoidmiseks pigem
näiteks muutujat nimega table. Näiteks nii:

int table;

table=10;

Muuseas, soovitan kirjutada programmi sees
muutujate nimed, kommentaarid jms alati inglise
keeles! See on hea edaspidises päris-töös,
kus väga tihti on vaja teha koostööd inimestega,
kes eesti keelt ei mõista. Programmi koodi
loetavus on programmeerimise-alase koostöö
jaoks hädavajalik.


Kuidas jäägiga jagada
----------------------

Nagu C keeles, nii ka javas on olemas täisarvude
jäägiga jagamise tehe % (protsendimärk).

Näiteks, 8 % 3 annab tulemuseks kahe: kaheksa
jäägi kolmega jagamisel.

Kuidas arvutada juhuslikku arvu
-------------------------------

Juhusliku arvu leidmiseks on Java teegis spetsiaalne
funktsioon juba olemas:
Math.random()
See funktsioon annab juhusliku suurusega, komaga
arvu 0-i ja 1-e vahel. (0 võib tulla, 1 ei tule).

Kuidas saada juhuslik täisarv 1-e ja X-i vahel
(st lubatud väärtused oleks 1,2,...,X):

korruta Math.random() tulemus arvuga X 
(siis saad arvu vahemikus 0,...,X kus X ei
saa siiski tulla) liida tulemusele 1 (et
ei tuleks 0 ja saaks tulla X) ja võta saadud 
arvust täisosa tüübiteisendust tegeva
nn "cast"-i (int) abil:

tulemus = (int)((Math.random()*X)+1)


Kuidas programmi kirjutamisega hakkama saada
--------------------------------------------

Kui asi tundub veidi keeruline, siis järgi neid
põhimõtteid, mis on programmeerimise juures
alati abiks (eriti veel suuremate ja tõsisemate
programmide puhul, kus nende põhimõtete eiramine
viib üldiselt fiaskoni):

- Ära proovi kõike kohe valmis kirjutada.
  
  Selle asemel kirjuta algul hoopis lihtne
  variant programmist, näiteks lihtne tikumäng,
  kus alati N on algul 10, M on alati 3, 
  inimmängija käike ei kontrollita, mäng
  toimub ainult ühe korra (uuesti ei alustata).
  
  Proovi see lihtsam programm korralikult
  käima saada.
  
  Kui selle lihtsama programmi kirjutamine läheb
  kah aeglaselt, siis tee hakatuseks veel lihtsam:
  
  Tee algul näiteks programm, kus arvuti võtab
  alati ühe tiku ja vastane üldse ei käi. Selge,
  et kümne käiguga on siis masin võitnud. Las
  programm trükib iga sammu järel oma käigu
  ja seisu ka. Kui see programm töötab, alles
  siis asu lisama vastase käigu lugemist. Ära
  kontrolle esialgu kohe peale pane ja ära esialgu
  pane arvutit juhuslikku käiku arvutama (las
  võtab alati ühe tiku). Enne, kui programmiga
  edasi lähed, kompileeri ja pane käima ja testi,
  et lisandusega lihtne variant käib OK.
  
- Kui see lihtsam programm käib OK, alles siis
  asu lisandusi tegema: 
  
  - juhusliku käigu leidmine
  - M ja N valik,
  - inimmängija käikude kontroll, 
  - inimmängija võidu / kaotuse tuvastamine
    ja väljatrükk
  - mängu uuesti alustamine
  - jne
  
  ning iga lisanduse järel kompileeri ja paranda
  ja testi, kuni see lisandus töötab OK. Alles
  seejärel asu programmeerima uut lisandust!
  
  
 - Küsimus inimmängija käigu kontrollimisest.
 
   Kuna inimmängija võib püüda vale käiku teha
   mitte ainult ühe korra, vaid näiteks sada
   korda järjest, siis kuidas seda kontrollida?

   Vastus: inimmängija käigu lugemine tuleb panna
   omaette väikesesse tsüklisse, mis lõpeb siis,
   kui anti OK, reeglipärane käik.
 
 
 - Küsimus mängu uuesti alustamisest: kuidas
   päris algusse tagasi minna?

   Mängu enda tsükli ümber tuleb teha veel
   üks tsükkel, kus iga tsükli kord tähendab
   ühte täismängu. Tsükkel lõpeb siis, kui
   inimmängija enam uut mängu mängida ei taha.
   
   NB! Kui olemasoleva tsükli ümber uue paned,
   siis ära unusta sisemise programmiteksti
   taanet igal pool kenasti paremale nihutamast!
   
Disclaimer
==========

Kui mõni soovitus või nõuanne siin tekstis tundub
mitte aitavat või tundub otseselt vale, siis
seda nõuannet ei pea arvestama! Lõppkokkuvõttes
loeb ainult see, et programm oleks
tehtud vastavalt alguses toodud nõuetele ja
töötaks OK. Vajadusel eksperimenteeri ja
katseta ise, loe õpikut ja juhendeid, vaata
näiteid jne.