Connect4 leírása

Az alábbi leírás a Smalltalk-ban írt Connect4 játékról szól. A játékban egy 6x7-es játékmezőn helyeznek el a játékosok színes korongokat, úgy hogy az mindig az adott oszlop tetejére kerüljön. Egy játékos nyert, ha egy sorban, oszlopban vagy átlósan van 4 korongja. A programnak három osztálya van, a továbbiakban ezek leírása következik.

Connect4Shell osztály

A Connect4Shell osztály a Shell osztály leszármazottja. Ez a játék főablaka. Tartalmazza a játékmezőt, egy gombot, amire kattintva elindul a játék, valamint egy színes mezőt, ami jelzi a következő játékost.

Fontosabb metódusok:

createComponents: Ezt a metódust a Shell-től örökölte az osztály, létrehozza a komponenseket, regisztrálja a presentereket a játéktáblát kirajzoló boardView és következő játékost mutató colorView elemekhez.

createComponents super createComponents. boardView := self add: Connect4Board new name: 'boardView'. colorView := self add: ColorPresenter new name: 'colorView'.

createSchematicWiring: Ez állítja be az eseménykezelést. #gameOver fogadásakor a játék végkimenetelét üzenetablakkal jelző #onGameOver, #hasStepped esetén pedig a colorView színét a következő játékos színére váltó #onStepped hívódik meg.

createSchematicWiring super createSchematicWiring. boardView when: #gameOver send: #onGameOver to: self. boardView when: #hasStepped send: #onStepped to: self.

startButtonClicked: A játék kezdése gombra való kattintáskor hívódik meg. Engedélyezi a táblára való kattintást, újrarajzolja a táblát és inicializálja a modellt.

startButtonClicked boardView view isEnabled: true. boardView view canvas erase. boardView drawBoardBackground. boardView model initialize. colorView value: Color red.

Connect4Board osztály

A Connect4Board osztály a Presenter osztály leszármazottja. Ez felel a játéktábla és a rajta található korongok kirajzolásáért.

Fontosabb metódusok:

initialize : Ezzel inicializáljuk a táblát. A Presenter ősosztály inicializáló művelete mellett beállítja a játék logikájáért felelős model adattagot a Connect4Model egy példányára, beállítja, hogy mekkora méretűek legyenek a korongok, és kirajzolja a tábla hátterét.

initialize super initialize. model := Connect4Model new. model initialize. size := 50. spacing := 5. self drawBoardBackground.

drawBoardBackground: Ez felel a tábla hátterének kirajzolásáért. Először egy kék téglalapot rajzol ki, majd elhelyezi rajta a fehér köröket, amik az üres helyeket mutatják.

drawBoardBackground self view canvas pen: Pen blue; brush: (Brush color: Color blue); rectangle: (0@0 extent: 385@330). 1 to: 7 do: [ :i | 1 to: 6 do: [:j | self view canvas pen: Pen blue; brush: Brush white; ellipse: (Rectangle origin: ((i-1)*(size+spacing))@(330-((j)*(size+spacing)))extent: size@size) ] ].

onLeftButtonPressed: A táblára való kattintást kezeli le. A kör mérete (size) és a körök között hagyott hely (spacing) alapján kiszámoljuk, hányadik oszlopra kattintott a játékos majd a modellben végrehajtjuk a lépést és újrarajzoljuk a korongokat. Végül a #hasStepped eseményt hívjuk meg, és leellenőrizzük, hogy vége van-e a játéknak, és ha igen, azt a #gameOver eseménnyel jelezzük.

onLeftButtonPressed: aMouseEvent |col brush| col := ((aMouseEvent x) // (size+spacing))+1. (col <= 7) ifTrue: [ model doStep: col. self view canvas erase. self drawBoardBackground. 1 to: 7 do: [ :i | 1 to: (model getColumnHeight: i ) do: [:j | ((model getPieceAtCol: i row: j ) = 0) ifTrue: [brush := Brush color: Color red] ifFalse: [brush := Brush color: Color yellow]. self view canvas pen: Pen blue; brush: brush; ellipse: (Rectangle origin: ((i-1)*(size+spacing))@(330-((j)*(size+spacing)))extent: size@size) ] ]. self trigger: #hasStepped. (model isOver) ifTrue: [ self trigger: #gameOver. ]. ].

Connect4Model osztály

A Connect4Model osztály az Object leszármazottja. Ez kezeli a játék háttérlogikáját.

Fontosabb metódusok:

initialize: Inicializáljuk a modellt egy üres tábla (gameField) létrehozásával, valamint a kezdőjátékos beállításával.

initialize super initialize. gameField := Array new: 7. gameField := gameField collect: [:i |Array new: 6]. currentPlayer := 0. isOver := false.

doStep: pos Végrehajtunk egy lépést a játékban: a pos indexű oszlop tetejére beteszi a jelenlegi játékos elemét, ha az oszlop még nincs tele és leellenőrzi az eredményt

doStep: pos (self putPiece: currentPlayer atPos: pos ) ifTrue: [ currentPlayer := (currentPlayer +1) \\ 2. self checkResultwithCol: pos Row: (self getColumnHeight: pos ). ].

putPiece: pos Elhelyezzük a megadott játékos egy korongját a pos indexű oszlopba. Ehhez először lekérdezzük, hogy az adott oszlopba lehet-e még egyáltalán tenni (a 6., tehát utolsó indexű elem null-e?) és ha igen, megkeressük az első üres elemet és beállítjuk az értékét a játékos számára.

putPiece: player atPos: pos |currentCol i| currentCol := gameField at: pos. i := 1. (currentCol at: 6) ifNil: [ [(i >= 6) | ((currentCol at: i) = nil).] whileFalse: [i := i+1.]. currentCol at: i put: player. gameField at: pos put: currentCol. ^true ] ifNotNil: [^false].

doStep: pos Végrehajtunk egy lépést a játékban: a pos indexű oszlop tetejére beteszi a jelenlegi játékos elemét, ha az oszlop még nincs tele és leellenőrzi az eredményt

doStep: pos (self putPiece: currentPlayer atPos: pos ) ifTrue: [ currentPlayer := (currentPlayer +1) \\ 2. self checkResultwithCol: pos Row: (self getColumnHeight: pos ). ].

checkResultwithCol: col Row: row Leellenőrizzük, hogy az adott sorba és oszlopba helyezett korong lerakása után mi lett az eredmény. Mivel oszlopnak csak tetejére rakhatunk korongot, ezért ott csak lefelé kell ellenőrizni, amúgy pedig sorban és átlósan is mondkét irányba. Igazzal térünk vissza, ha véget ért a játék, azaz valaki nyert vagy döntetlen.

checkResultwithCol: col Row: row "Leellenõrzi, hogy a col oszlopba row helyre lerakott elem után van-e végeredmény" |matches tempCol tempRow piece tempOver| piece := self getPieceAtCol:col row: row. "az oszlopban csak lefelé kell ellenõrizni" matches := 1. tempCol := col. tempRow := row-1. [(tempRow >= 1) and: [(self getPieceAtCol:tempCol row: tempRow) = piece]] whileTrue: [matches := matches + 1. tempRow := tempRow-1]. (matches >= 4) ifTrue: [isOver := true. ^true]. "sorban ellenõrzés balra, majd jobbra" matches := 1. tempCol := col-1. tempRow := row. [(tempCol >= 1) and: [(self getPieceAtCol:tempCol row: tempRow) = piece]] whileTrue: [matches := matches + 1. tempCol := tempCol-1]. tempCol := col+1. tempRow := row. [(tempCol <= 7) and: [(self getPieceAtCol:tempCol row: tempRow) = piece]] whileTrue: [matches := matches + 1. tempCol := tempCol+1]. (matches >= 4) ifTrue: [isOver := true. ^true]. "átlósan bal lentrõl jobb fentre" matches := 1. tempCol := col-1. tempRow := row-1. [(tempCol >= 1) & (tempRow >= 1) and: [(self getPieceAtCol:tempCol row: tempRow) = piece]] whileTrue: [matches := matches + 1. tempCol := tempCol-1. tempRow := tempRow-1]. tempCol := col+1. tempRow := row+1. [(tempCol <= 7) & (tempRow <= 6) and: [(self getPieceAtCol:tempCol row: tempRow) = piece]] whileTrue: [matches := matches + 1. tempCol := tempCol+1. tempRow := tempRow+1]. (matches >= 4) ifTrue: [isOver := true. ^true]. "átlósan bal fentrõl jobb lentre" matches := 1. tempCol := col-1. tempRow := row+1. [(tempCol >= 1) & (tempRow <= 6) and: [(self getPieceAtCol:tempCol row: tempRow) = piece]] whileTrue: [matches := matches + 1. tempCol := tempCol-1. tempRow := tempRow+1]. tempCol := col+1. tempRow := row-1. [(tempCol <= 7) & (tempRow >= 1) and: [(self getPieceAtCol:tempCol row: tempRow) = piece]] whileTrue: [matches := matches + 1. tempCol := tempCol+1. tempRow := tempRow-1]. (matches >= 4) ifTrue: [isOver := true. ^true]. "tele van-e minden oszlop" tempOver := true. (self gameField) do: [:c|tempOver := tempOver & ((self getColHeight: c) = 6)]. ^tempOver.

Készitette : Sisa Attila