Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: Hollywood: Miinaharava

jalski [14.06.2012 15:25:14]

#

Aloittelijalle miinaharavan toteutus on hyvä ja kohtuullisen palkitseva harjoitus.

Ohessa rakenteeltaan äärimmäisen yksinkertainen, mutta täysi miinaharava pelin toteutus kirjoitettuna Hollywoodilla. Oleelliset kohdat koodista kääntyvät helposti mille tahansa ohjelmointikielelle, joten pelin perusrunko on valmiina käytettäväksi omiin projekteihin.

lähdekoodit ja binäärit täältä

Piirtorutiinien toteutuksen olen mallintanut Infernolta, koska olen niihin jotenkin kiintynyt... ;-)

;
; Hollywood Mine Sweeper v. 0.2
;
; Jali Heinonen
; jali.heinonen@gmail.com
;
@APPICON {      Ic16x16 = "icons/my16x16icon.png",
                        Ic24x24 = "icons/my24x24icon.png",
                        Ic32x32 = "icons/my32x32icon.png",
                        Ic48x48 = "icons/my48x48icon.png",
                        Ic128x128 = "icons/my128x128icon.png",
                        Ic256x256 = "icons/my256x256icon.png",
                        Ic512x512 = "icons/my512x512icon.png" }

@DISPLAY { Title = "Hollywood Mine Sweeper v. 0.2", Width = 384, Height = 384+32, Hidden = True }

; Include game framework
@INCLUDE "hgf/game.hws"

@BRUSH 1, "data/tile.png", {LoadAlpha = True}
@BRUSH 2, "data/button_down.png", {LoadAlpha = True}
@BRUSH 3, "data/mine.png", {LoadAlpha =  True}
@BRUSH 4, "data/button_up.png", {LoadAlpha = True}

@BRUSH 8, "data/explosion.png", {LoadAlpha = True}
@BRUSH 9, "data/flag.png", {LoadAlpha =  True}

@BRUSH 10, "data/n0.png", {LoadAlpha = True}
@BRUSH 11, "data/n1.png", {LoadAlpha = True}
@BRUSH 12, "data/n2.png", {LoadAlpha = True}
@BRUSH 13, "data/n3.png", {LoadAlpha = True}
@BRUSH 14, "data/n4.png", {LoadAlpha = True}
@BRUSH 15, "data/n5.png", {LoadAlpha = True}
@BRUSH 16, "data/n6.png", {LoadAlpha = True}
@BRUSH 17, "data/n7.png", {LoadAlpha = True}
@BRUSH 18, "data/n8.png", {LoadAlpha = True}

; Board size, internally board is 2 tiles larger
Const #BRDSZ = 12
Const #BRDSZI = #BRDSZ + 2

; Board tile states
Const #UNSELECTED = 1
Const #SELECTED = 2
Const #MARKED = 4

; Skill level
Const #EASY = 25


Function init_board()
        Local i, j
        Local row, column

        finished = False
        score = 0
        mines = 0

        ; initialize board
        board = {}
        For i = 0 To #BRDSZI - 1
                board[i] = {}
                For j = 0 To #BRDSZI - 1
                        board[i][j] = {}
                        board[i][j].mine = 0
                        board[i][j].state = #UNSELECTED
                Next
        Next

        ;place mines
        For i = 0 To #EASY - 1
                j = Rnd(#BRDSZ*#BRDSZ)
                row = Int((j/#BRDSZ)+1)
                column = Int((j%#BRDSZ)+1)
                If board[row][column].mine = 0
                        board[row][column].mine = 1
                        mines = mines + 1
                EndIf
        Next
EndFunction


Function display_square(i, j, img)
        game.buffer:drawImg(img.r:addpt({ x = (j-1) * img.r:dx(), y = i * img.r:dy() }), img, Point:new())
EndFunction


Function display_mines()
        For Local i = 1 To #BRDSZ
                For Local j = 1 To #BRDSZ
                        If board[i][j].mine = 1 Then display_square(i, j, img3)
                Next
        Next
EndFunction


Function display_score()
        Local blankr = Rect:new({ min = { x = 140, y = 2}, max = { x = 384, y = 30 } })
        Local score_text$ = "score: " ..PadNum(score, 3)
        Local percentage$ = PadNum(Int(score/((#BRDSZ*#BRDSZ)-mines)*100), 3) .."%"

        SetFont(#MONOSPACE, 20)
        SetFontStyle(#BOLD|#ANTIALIAS)
        SetFontColor(#BLACK)
        SetFillStyle(#FILLCOLOR)
        game.buffer:drawRect(blankr, $CCCCCC)
        game.buffer:drawText({ x = 150, y = 6 }, percentage$)
        game.buffer:drawText({ x = 250, y = 6 }, score_text$)
EndFunction


Function board_check(i, j)
        If board[i][j].mine = 1 Then Return(-1)
        If board[i][j].state & (#SELECTED|#MARKED) Then Return(-2)
        Local c = 0
        For Local row = i-1 To i+1
                For Local col = j-1 To j+1
                        If board[row][col].mine = 1 Then c = c + 1
                Next
        Next
        Return(c)
EndFunction


Function mark_square(i, j)
        Switch board[i][j].state
        Case #UNSELECTED:
                board[i][j].state = #MARKED
                display_square(i, j, img7)
        Case #MARKED
                board[i][j].state = #UNSELECTED
                display_square(i, j, img6)
        EndSwitch
EndFunction


Function display_zeros(i, j)
        For Local row = i-1 To i+1
                For Local col = j-1 To j+1
                        If row < 1 Or row > #BRDSZ Or col < 1 Or col > #BRDSZ Then Continue
                        Local tile = board_check(row, col)
                        If tile = 0
                                score = score + 1
                                board[row][col].state = #SELECTED
                                display_square(row, col, images[tile])
                                display_zeros(row, col)
                        Else
                                If tile >= 1 And tile <= 8
                                        board[row][col].state = #SELECTED
                                        display_square(row, col, images[tile])
                                        score = score + 1
                                EndIf
                        EndIf
                Next
        Next
EndFunction


Function init_gui()
        MakeButton(1000, #SIMPLEBUTTON, 10, 2, img4.r:dx(), img4.r:dy(), { OnMouseDown = menu_handler, OnMouseUp = menu_handler, OnMouseOver = menu_handler, OnMouseOut = menu_handler })
        Local num_button = 1
        Local tiler = Rect:new({min = { x = 0, y = 0 }, max = { x = 32, y = 32 }})
        Local pos = Point:new({ x = 0, y = 32 })

		; Create buttons
        For Local i = 1 To #BRDSZ
                For Local j = 1 To #BRDSZ
                        MakeButton(num_button, #SIMPLEBUTTON, tiler:addpt(pos).min.x, tiler:addpt(pos).min.y, tiler:addpt(pos):dx(), tiler:addpt(pos):dy(), { OnMouseUp = board_handler, OnRightMouseUp = board_handler, OnMouseOver = board_handler, OnMouseOut = board_handler })
                        pos.x = pos.x + tiler:dx()
                        num_button = num_button + 1
                Next
                pos.x = 0
                pos.y = pos.y + tiler:dy()
        Next
EndFunction


Function draw_board()
        Local panelr = Rect:new({ min = { x = 0, y = 0 }, max = { x = 384, y = 32 } })
        SetFillStyle(#FILLCOLOR)
        game.buffer:drawRect(panelr, $CCCCCC)
        game.buffer:drawImg(img4.r:addpt({ x = 10, y = 2 }), img4, Point:new())

        display_score()

        Local pos = Point:new({ x = 0, y = 32 })

		; Draw button tiles
        For Local i = 1 To #BRDSZ
                For Local j = 1 To #BRDSZ
                        game.buffer:drawImg(img1.r:addpt(pos), img1, Point:new())
                        pos.x = pos.x + img1.r:dx()
                Next
                pos.x = 0
                pos.y = pos.y + img1.r:dy()
        Next
EndFunction


Function menu_handler(msg)
        Switch msg.action
                Case "OnMouseDown":
                        game.buffer:drawImg(img2.r:addpt({ x = 10, y = 2 }), img2, Point:new())
                Case "OnMouseUp":
                        finished = False
                        map = init_board()
                        draw_board()
                        game.buffer:drawImg(img10.r:addpt({ x = 10, y = 2 }), img10, Point:new())
                Case "OnMouseOver":
                        game.buffer:drawImg(img10.r:addpt({ x = 10, y = 2 }), img10, Point:new())
                Case "OnMouseOut":
                        game.buffer:drawImg(img4.r:addpt({ x = 10, y = 2 }), img4, Point:new())
        EndSwitch
EndFunction


Function board_handler(msg)
        If finished Then Return
        Local by = Int(msg.id / #BRDSZ)
        Local bx = msg.id % #BRDSZ
        If bx = 0 Then bx = #BRDSZ Else by = by + 1
        Switch msg.action
                Case "OnMouseUp":
                        ; Handle game input
                        Local i = board_check(by,bx)
                        If i = -1 And board[by][bx].state <> #MARKED
                                display_mines()
                                display_square(by, bx, img8)
                                finished = True
                        ElseIf i >= 0 And i <= 8
                                score = score + 1
                                board[by][bx].state = #SELECTED
                                display_square(by, bx, images[i])
                                If i = 0 Then display_zeros(by, bx)
                                display_score()
                                If score + mines = #BRDSZ*#BRDSZ
                                        display_mines()
                                        finished = True
                                Endif
                        EndIf
                Case "OnRightMouseUp"
                        mark_square(by, bx)
                Case "OnMouseOver":
                        If board[by][bx].state = #SELECTED Then Break
                        If board[by][bx].state = #MARKED Then display_square(by, bx, img7) Else display_square(by, bx, img6)
                Case "OnMouseOut":
                        If board[by][bx].state = #SELECTED Then Break
                        If board[by][bx].state = #MARKED Then display_square(by, bx, img9) Else display_square(by, bx, img1)
        EndSwitch
EndFunction



; Create tinted images for selection highlighting
CopyBrush(1, 6)
TintBrush(6, #WHITE, 128)
CopyBrush(9, 7)
TintBrush(7, #WHITE, 128)

; Create tinted image for menu GUI button highlighting
CopyBrush(4, 19)
TintBrush(19, #WHITE, 128)

; Create game images
img1 = Image:new(1)
img2 = Image:new(2)
img3 = Image:new(3)
img4 = Image:new(4)

img6 = Image:new(6)
img7 = Image:new(7)

img8 = Image:new(8)
img9 = Image:new(9)

img10 = Image:new(19)

images = {}
images[0] = Image:new(10)
images[1] = Image:new(11)
images[2] = Image:new(12)
images[3] = Image:new(13)
images[4] = Image:new(14)
images[5] = Image:new(15)
images[6] = Image:new(16)
images[7] = Image:new(17)
images[8] = Image:new(18)


; Init game, draw the board and GUI
init_gui()
init_board()
game.buffer:init()
draw_board()
OpenDisplay(1)

; Process events
Repeat
        WaitEvent
Forever

mietinvaan [30.10.2012 19:02:22]

#

Jos oletetaan tämän koodivinkin kohdeyleisönä olevan aloittelijat, jotka eivät lähtökohtaisesti osaa itse miinaharavaa toteuttaa, niin se on melko huono. Jos on tarkoituksena opettaa, niin täytyisi kertoa _miksi_ asioita tehdään niin kuin tehdään. Kommentointi ei tuossa ole sen mukaista.

Loppupuolella kaivattaisiin myös lisää silmukkoja ja esim. init board funktiossa alustus ja miinojen asettelu olisi helposti yhdistettävissä lyhyemmäksi koodiksi. En tätä testannut, mutta se kokonaisuutena näyttää siltä, että sait sen juuri kasaan ja pullautit sitten suoraan tänne näkyville. Ei siinä mitään pahaa sinänsä, on aina hienoa saada jotain aikaan, mutta opetusmateriaaliksi tästä ei mielestäni ole ilman monen osan uudelleenkirjoitusta tai -jäsentelyä.

jalski [05.11.2012 12:11:30]

#

mietinvaan kirjoitti:

Loppupuolella kaivattaisiin myös lisää silmukkoja ja esim. init board funktiossa alustus ja miinojen asettelu olisi helposti yhdistettävissä lyhyemmäksi koodiksi.

Mitähän höpiset? Tuossa init_board() funktiossa nollataan ensin pelilaudan tila ja arvotaan sitten sopiva määrä miinoja pelialueelle. Mitähän meinasit tuossa yhdistellä lyhyemmäksi koodiksi?

Jos tietää miten Hollywood-ohjelma prosessoi tapahtumia, niin ohjelman kulkua on kuitenkin aika helppo seurata. En hirveästi näe järkeä kommentoida jokaista riviä lyhkäisissä funktioissa, joiden nimet sinällään jo kertovat mitä tehdään.

Metabolix [05.11.2012 14:48:57]

#

Minusta tämä on muuten kohtuullisen selvä vinkki, mutta BRDSZ(I) on epäselvä nimi ja myös kuvien nimet kannattaisi vaihtaa järkeviksi eli kuvan sisältöä vastaaviksi.

jalski [25.12.2012 16:10:40]

#

Fortran95 versio löytyy täältä ja MiniBASIC versio täältä

jalski [12.01.2014 09:48:30]

#

Miinaharavan PL/I:llä kirjoiteltu Windows versio löytyy täältä.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta