def pelaa_miinaharavaa(leveys, korkeus, alue, x, y): koordinaatit = [(x, y)] while len(koordinaatit) > 0: x, y = koordinaatit.pop() if alue[y][x] == "x": print("Astuit miinaan!") print("Peli loppui!") break elif alue[y][x] == " ": alue[y][x] == "o" for vaaka in range((x - 1), (x + 2)): for pysty in range((y - 1), (y + 2)): if vaaka >= 0 and vaaka <= (leveys - 1) and pysty >= 0 and pysty <= (korkeus - 1): koordinaatit.append((vaaka, pysty)) pelaa_miinaharavaa(leveys, korkeus, alue, x, y)
Eli olin tekemässä ohjelmoinnin kurssia varten miinaharavaa. Ongelmaksi muodostui se, että nyt peli ilmoittaa pelaajan osuneen aina miinaan, vaikkei niin olisi käynytkään. Ongelma on ilmeisesti ylläolevassa pätkässä, koska elif-lauseke lisää myös x:n sisältävät ruudut koordinaatit-listaan. Yritin erilaisia vaaka == " " and pysty == " ", mutten keksinyt ratkaisua.
Eiks toi nyt lisäile koordinaatit myös valitun pisteen ympäriltä ja testaa ne sekä sen jälkeen lisää koordinaatit niiden ympäriltä ja testaa ne ja jälleen lisäilee koordinaatit niiden ympäriltä ja testaa ne. Eli siis olettaisin että toi koodi sanoo "astuit miinaan" aina jos kentällä ylipäätään on miina.
Toi for vaaka ja for pysty hässäkkä on ilmeisesti sitä varten, että se laskisi niitä miinojen määriä reuna-alueilla, mutta nyt kun se ei laske, niin homma ei toimi.
Käyttäisin kahta erillistä taulukkoa.
Toinen sisältäisi tiedon, onko ruudussa tai sen naapureissa miinaa ('x' tai 0-8). Sen sisältö luotaisiin heti pelin alussa, kun miinat sijoitetaan kentälle.
Toinen, tulostukseen käytettävä taulukko sisältäisi ruudussa kulloinkin näytettävän tiedon (jokin merkeistä 'x 012345678' tai pelaajan asettama apumerkki).
Itse ongelma varmaan ratkeaisi muuttamalla for-silmukassa olevaa ehtolausetta:
if 0 <= vaaka < leveys and 0 <= pysty < korkeus and alue[pysty][vaaka] == ' ': koordinaatit.append((vaaka, pysty))
Tietenkään ei siis pidä verrata vaaka- tai pysty-muuttujaa tiettyyn tekstiin, vaan pitää hakea ruudun sisältö alue-muuttujasta, kuten aiemmissakin vastaavissa kohdissa.
Jos koodisi vastaa muuten tehtävänantoa, tarkoituksena näyttäisi olevan kylläkin alueen täyttäminen (flood fill) eikä miinaharavan pelaaminen, kun miinaharavan idea on kuitenkin siinä, että miinojen sijainnit eivät ole pelaajan tiedossa...
Metabolix kirjoitti:
Jos koodisi vastaa muuten tehtävänantoa, tarkoituksena näyttäisi olevan kylläkin alueen täyttäminen (flood fill) eikä miinaharavan pelaaminen, kun miinaharavan idea on kuitenkin siinä, että miinojen sijainnit eivät ole pelaajan tiedossa...
No kyllähän miinaharavassa yleensä on tuollainen "flood fill" kun klikkaa aution alueen keskelle. Sen vaan pitäisi sitten pysäyttää niihin numeroihin eikä mennä miinoihin asti.
Grez kirjoitti:
No kyllähän miinaharavassa yleensä on tuollainen "flood fill" kun klikkaa aution alueen keskelle.
On, mutta sen nimi ei yleensä ole "pelaa_miinaharavaa", ja se ei etene kaikista pienistäkin raoista vaan juurikin pysähtyy jo ensimmäiseen numeroruutuun.
Mun mielestä se kyllä hakee kaikki numeroruudut siitä ympäriltä ja pysähtyy vasta kun pidemmälle ei enää pääse, eli numeroiden välissä ei ole "pienintäkään rakoa". Eli tosta koodista puuttuu kokonaan se numeroiden muodostaminen, niin eihän se tietty niihin voi pysähtyä.
Tarkoitin juurikin, että kun aloittajan koodissa ei näytä olevan numeroita ollenkaan, hänen algoritminsa etenee mistä tahansa miinojen välistä (jopa kulmittain), kun pitäisi pysähtyä jo ruutua aiemmin numeroruutuihin. Myöskään miinaharavan haku ei taida hyppiä kulmittain numeroiden välistä.
Itse käyttäisin yhtä taulukkoa, mikä olisi yhden ruudun leveämpi ja korkeampi kuin varsinainen pelialue (pysäyttää haun ilman lisätarkistuksia). Peliruudun tilan tallentaisin UDT:hen, missä olisi tieto onko ruudussa miinaa ja ruudun tila (valitsematon, valittu tai merkattu).
Sitten tarvitaan vain pari funktiota, joiden avulla "flood fill" hoituu yksinkertaisella rekursiolla. Esim. PL/I:llä:
/******************************************************************************************************/ board_check: proc(i, j) returns(type INT); dcl (i, j, row, col, c) type INT; if board(i,j).mine then return(-1); if iand(board(i,j).state, ior(binvalue(SELECTED), binvalue(MARKED))) ^= 0 then return(-2); c = 0; do row = i-1 to i+1; do col = j-1 to j+1; if board(row,col).mine then c += 1; end; end; return(c); end board_check; /******************************************************************************************************/ display_zeros: proc(hdc, i, j) recursive; dcl hdc type HDC; dcl (i, j, row, col, tile) type INT; do row = i-1 to i+1; do col = j-1 to j+1; if row < 1 | row > brdsz | col < 1 | col > brdsz then iterate; tile = board_check(row, col); if tile = 0 then do; score += 1; board(row,col).state = binvalue(SELECTED); call display_square(hdc, row, col, htilesbm(tile)); call display_zeros(hdc, row, col); end; else if tile >= 1 & tile <= 8 then do; board(row,col).state = binvalue(SELECTED); call display_square(hdc, row, col, htilesbm(tile)); score += 1; end; end; end; end display_zeros;
Ruudukkopeleissä on mielestäni selkeää erotella puhtaasti ruudukkoon liittyvät yleiset toiminnot omaan luokkaansa, jotta pelikohtainen koodi pääsee keskittymään itse asiaan.
Tein huvikseni miinaharavan itsekin, tässä siitä pari osaa:
ruudukko.py:
class Ruudukko: # ... def ruudukossa(self, x, y): return (0 <= y < self.kork) and (0 <= x < self.lev) def naapurit(self, x, y, viist=True): nxy = NAAPURIT_SUORAT + (NAAPURIT_VIISTOT if viist else ()) for dx, dy in nxy: rx, ry = x + dx, y + dy if self.ruudukossa(rx, ry): yield rx, ry
miinaharava.py:
class Miinaharava: # ... # kutsutaan pelin alustusvaiheessa yksittäistä miinaa asetettaessa def _numeroi_miinaymp(self, x, y): for rx, ry in self.data.naapurit(x, y): if self.data.ruudut[ry][rx] != MIINA: self.data.ruudut[ry][rx] += 1 def _avaa_nolla_alue(self, x, y): avattavat = [(x, y)] while avattavat: x, y = avattavat.pop() if self.naytto.ruudut[y][x] != KIINNI: continue if self.data.ruudut[y][x] == 0: avattavat.extend(self.data.naapurit(x, y)) self.naytto.ruudut[y][x] = self.data.ruudut[y][x]
Chiman kirjoitti:
Ruudukkopeleissä on mielestäni selkeää erotella puhtaasti ruudukkoon liittyvät yleiset toiminnot omaan luokkaansa, jotta pelikohtainen koodi pääsee keskittymään itse asiaan.
Miinaharavan ollessa kyseessä toteutukseen tosin riittää yksinkertaisesti taulukko ja muutama aliohjelma, joten luokkien vääntäminen tuntuu minusta hiukan turhalta hienostelulta.
PL/I:llä kirjoiteltu Win32 miinaharava äksönissä
Chiman kirjoitti:
Tein huvikseni miinaharavan itsekin, tässä siitä pari osaa:
Mikset sisäisesti ympäröisi peliruudukkoa tyhjillä ruuduilla ja hankkiutuisi eroon ruudukossa() funktiosta ja turhista tarkistuksista?
jalski kirjoitti:
Mikset sisäisesti ympäröisi peliruudukkoa tyhjillä ruuduilla ja hankkiutuisi eroon ruudukossa() funktiosta ja turhista tarkistuksista?
Makuasia. Pidän enemmän siitä, että tietorakenteet sisältävät vain varsinaista dataa.
Aihe on jo aika vanha, joten et voi enää vastata siihen.