https://ujk-ujk.blogspot.com/2025/04/wxruby3-ile-masaustu-uygulama.html
Selam, WxRuby ile masaüstü uygulama geliştirme yazı dizimizin son bölümünde bir Tetris oyunu yazacağız nasipse. Yavaş yavaş sindire sindire adım adım uygulamamızı oluşturacağız.
Haydi başlayalım.
Tetris Oyunu
Öncelikle bir uygulama iskeleti oluşturarak başlayalım. Küçük kibar bir frame ile başlayalım.
tetris.rb
require "wx"
class Tetris < Wx::Frame
def initialize(parent)
super(parent, size: [180,380],
style: Wx::DEFAULT_FRAME_STYLE ^ Wx::RESIZE_BORDER ^ Wx::MAXIMIZE_BOX)
init_UI
end
def init_UI
set_title "Tetris"
centre
@statusbar = create_status_bar
@statusbar.set_status_text '0'
end
end
Wx::App.run {
Tetris.new(nil).show
}
Dikey, uzunlamasına bir pencere, default pencere stilinden boyutlandırılabilirlik ve maximize kutusu çıkarılmış. Temizlenen satırların sayısını durum çubuğuna yazacağız, şimdilik sıfır yazıyoruz.
Oyun tablamızı bir Wx::Panel nesnesi olarak penceremize ekleyeceğiz, ama içinde çok fazla kod olacağını düşündüğüm için ona ayrı bir sınıf tanımı yazalım.
class Board < Wx::Panel
BoardWidth = 10
BoardHeight = 22
Speed = 300
ID_TIMER = 1
def initialize(*args)
super(*args)
init_board
end
def init_board
end
end
Board sınıfımızı Wx::Panel'den türettik. BoardWidth oyun alanımızın kaç kare genişliği olacağı , yani bir satırda 10 göz olacak. BoardHeight değeri de alanımızın yüksekliği kaç göz olacağı, yani 22 göz yükseklik olacak. Speed değeri zamanlayıcının 300 ms'de bir kaydırma yapacağını belirtiyor. Bir de Timer nesnemiz olacak, onun da ID değerini 1 olarak belirleriz.
Şimdi bu Board nesnesinden bir tane penceremize ekleyelim.
class Tetris < Wx::Frame
def initialize(parent)
super(parent, size: [180,380],
style: Wx::DEFAULT_FRAME_STYLE ^ Wx::RESIZE_BORDER ^ Wx::MAXIMIZE_BOX)
init_UI
end
def init_UI
set_title "Tetris"
centre
@statusbar = create_status_bar
@statusbar.set_status_text '0'
@board = Board.new self
@board.set_focus
end
end
set_focus ile panele odaklanıyoruz ki tuş basılmalarını algılayarak oyunun senaryolarını çalıştıralım.
Parçalar neye benziyor?
Bu noktada biraz düşünelim. Board nesnemizi boyayarak hücrelerin içlerini mevcut tetris parçasına göre renklendirmek istiyoruz. Şekillerin biçimine odaklanmak yerine o gözde hangi parçanın bir gözü var ona odaklanmak daha iyi olacak. Çünkü silinmeler yaşandıkça parçaların bir kısmı silinebilir. Bu yüzden parçalar yukarıdan aşağı inerken bütün olsa da yerine yerleştiğinde onu hücrelerine ayırarak yerleştirmek mantıklı olacak. İlk önce olası şekiller hakkında bir taslak yapalım.

Üzerlerine numaralar yazdım. İşte hücreleri boyarken ne renk olacağını içine bu ilgili sayıyı koyarak renklendirme elde edeceğiz.
Şimdi oyun tablamızı doldurmaya başlayalım.
class Board < Wx::Panel
....
def init_board
@timer = Wx::Timer.new self, ID_TIMER
@board = [0,1,2,3,4,5,6,7]
evt_paint :on_paint
end
def on_paint(e)
end
end
İlk adımda bir Timer nesnesi üretiyoruz, bu zamanlayıcı parçaları hareket ettirirken kullanacağımız zamanlayıcı. @board oluşum değişkeninde tablamızdaki hücrelerin renkleri ve yukarıdaki tasarıma göre verilecek. Şimdilik başlangıçta boş olması gereken listeye deneme amacıyla her rengi koydum.
Şimdi on_paint metodu içinde sayfa ilk boyanırken bu renkleri o hücrelere koyacağız. @board değişkeni içinde sol alt köşeden başlamak üzere renk değerleri olacak. Sol alt köşe sıfır index, hemen sağı bir index, iki index olarak gidecek. 10 tane olunca bir üst sıraya gidilecek.
def on_paint(e)
paint do |dc|
size = get_client_size
board_top = size.height - BoardHeight * square_height
(0...BoardHeight).each do |i|
(0...BoardWidth).each do |j|
shape = shape_at j, BoardHeight - i - 1
if shape && shape != 0
draw_square dc,
0 + j * square_width,
board_top + i * square_height, shape
end
end
end
end
end
Burada bir tek hücrenin boyutlarını belirleyen square_width ve square_height metodları var, onları bir tanımlayalım.
def square_height
return client_size.height / BoardHeight
end
def square_width
return client_size.width / BoardWidth
end
Yüksekliği tamsayı olarak 22'ye genişliği de 10'a bölerek piksel olarak bir hücrenin genişlik ve yüksekliklerini buluyoruz.
Sırada verilen sütun ve satırdaki hücrenin içinde hangi şekil renginin olduğunu bulan shape_at metodu var.
# sütun, satır
def shape_at(col, row)
return @board[row * BoardWidth + col]
end
@board array değerlerinden verilen sütun ve satır numaralarına karşı gelen değer okunuyor ve geri dönülüyor, yani 0..7 arası bir değer. Bu shape_at daha birçok yerde kullanılacağı için bu tek satıra bir metod yazıp kodun daha anlaşılabilir olmasını sağladık.
Daha draw_square metodu var ama şimdilik kodu biraz anlayalım sonra draw_square metoduna geçelim.
size = get_client_size
board_top = size.height - BoardHeight * square_height
En aşağıdan başlıyor ya hücrelerimizin index değerleri, bu yüzden çizime başlamak için çizim alanının sol üst köşe koordinatı lazım. Bu koordinatın x değeri sıfır (sol kenar), y değeri de board_top değerimiz olacak , aslında square_height değerini tamsayı bölmesi ile hesapladığımıza göre bu rakam sıfır ila square_height arasında küçük bir düzeltme değeri olacaktır. Yani panelin en üst kenarına yakın bir değer.
(0...BoardHeight).each do |i|
(0...BoardWidth).each do |j|
Buraya göre i değişkeninde satır numarası (0'dan 21 dahile kadar bir değer) ve j değişkeninde sütun numarası ( 0'dan 9 dahile kadar bir değer) olacak, yani tüm çizim alanını hücre hücre tarıyoruz.
shape = shape_at j, BoardHeight - i - 1
Buna bakarsak ilk şeklimiz shape_at(0, 21) , yani @board[210] yani sol üst köşedeki hücrenin renk kodu.
if shape && shape != 0
draw_square dc,
0 + j * square_width,
board_top + i * square_height, shape
O hücreye çizim yapabilmemiz için iki koşul var birincisi o hücrede bir şekil kodu gelmiş olması, ki düşünürsek örnek verdiğimiz @board değişkeninde index 210 değeri yok ve isteyince nil döner, bu durumda çizim yapmaya gerek yok. İkinci koşulsa şekil kodunun sıfırdan farklı olması, bu durumda da çizime gerek yok, bu şekil kodunun sıfır olması kozunu hücreleri silerken falan kullanabiliriz.
draw_square metoduna verilen ilk argüman çizim yaptığımız dc nesnesi. İkinci argüman sol kenara göre o hücrenin x koordinatı, bunu belirtirken 0 + j * square_width şeklinde yazdım ki kendime de sıfırdan itibaren diye hatırlatayım. Üçüncü argüman hücrenin y koordinatı, o da board_top + i * square_height değerinde. Son argüman ise şekil kodu , yani renk numarası. Gelelim metoda
def draw_square(dc, x, y, shape)
colors = ['#000000', '#CC6666', '#66CC66', '#6666CC',
'#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
light = ['#000000', '#F89FAB', '#79FC79', '#7979FC',
'#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80',
'#80803B', '#803B80', '#3B8080', '#806200']
pen = Wx::Pen.new light[shape]
pen.cap = Wx::CAP_PROJECTING
dc.pen = pen
dc.draw_line x, y + square_height - 1, x, y
dc.draw_line x, y, x + square_width - 1, y
darkpen = Wx::Pen.new dark[shape]
darkpen.cap = Wx::CAP_PROJECTING
dc.pen = darkpen
dc.draw_line x + 1, y + square_height - 1,
x + square_width - 1, y + square_height - 1
dc.draw_line x + square_width - 1,
y + square_height - 1, x + square_width - 1, y + 1
dc.pen = Wx::TRANSPARENT_PEN
dc.brush = Wx::Brush.new colors[shape]
dc.draw_rectangle x + 1, y + 1,
square_width - 2, square_height - 2
end
Burada verilen hücre içine 3 boyutlu gibi görünen bir boyama yapıyoruz. colors array'inde hücre içinin rengi şekil koduna göre indexli olarak girilmiş. Şekiller taslağımızdaki numaralandırmaya bakarsanız oradaki numaralara karşı gelen renk kodlarını görürsünüz. light array'inde ışığa bakan kenarların çizgi rengi kodlarını, dark array'inde de gölge kalan kenar çizgileri renk kodunu görürüz.
pen = Wx::Pen.new light[shape]
pen.cap = Wx::CAP_PROJECTING
dc.pen = pen
dc.draw_line x, y + square_height - 1, x, y
dc.draw_line x, y, x + square_width - 1, y
Sol kenara bir çizgi ve üst kenara bir çizgiyi light array'indeki renk koduna göre çiziyoruz.
darkpen = Wx::Pen.new dark[shape]
darkpen.cap = Wx::CAP_PROJECTING
dc.pen = darkpen
dc.draw_line x + 1, y + square_height - 1,
x + square_width - 1, y + square_height - 1
dc.draw_line x + square_width - 1,
y + square_height - 1, x + square_width - 1, y + 1
Alt kenara ve sağ kenara birer çizgiyi dark array'indeki renk koduna göre çiziyoruz.
dc.pen = Wx::TRANSPARENT_PEN
dc.brush = Wx::Brush.new colors[shape]
dc.draw_rectangle x + 1, y + 1,
square_width - 2, square_height - 2
Kenar çizgisi olmayan bir dikdörtgeni, kenar çizgilerinin içinde kalan alana colors array'inde verilen renk koduyla çiziyoruz.
Programımız burada bizim girdiğimiz örnek şekil kodlarıyla bir şeyler çiziyor olmalı. Önce şu ana kadarki kodumuzu bir toptan görelim ve programımızı çalıştırıp, bakalım ne çiziyor.
tetris.rb
require "wx"
class Tetris < Wx::Frame
def initialize(parent)
super(parent, size: [180,380],
style: Wx::DEFAULT_FRAME_STYLE ^ Wx::RESIZE_BORDER ^ Wx::MAXIMIZE_BOX)
init_UI
end
def init_UI
set_title "Tetris"
centre
@statusbar = create_status_bar
@statusbar.set_status_text '0'
@board = Board.new self
@board.set_focus
end
end
class Board < Wx::Panel
BoardWidth = 10
BoardHeight = 22
Speed = 300
ID_TIMER = 1
def initialize(*args)
super(*args)
init_board
end
def init_board
@timer = Wx::Timer.new self, ID_TIMER
@board = [0,1,2,3,4,5,6,7]
evt_paint :on_paint
end
def draw_square(dc, x, y, shape)
colors = ['#000000', '#CC6666', '#66CC66', '#6666CC',
'#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
light = ['#000000', '#F89FAB', '#79FC79', '#7979FC',
'#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80',
'#80803B', '#803B80', '#3B8080', '#806200']
pen = Wx::Pen.new light[shape]
pen.cap = Wx::CAP_PROJECTING
dc.pen = pen
dc.draw_line x, y + square_height - 1, x, y
dc.draw_line x, y, x + square_width - 1, y
darkpen = Wx::Pen.new dark[shape]
darkpen.cap = Wx::CAP_PROJECTING
dc.pen = darkpen
dc.draw_line x + 1, y + square_height - 1,
x + square_width - 1, y + square_height - 1
dc.draw_line x + square_width - 1,
y + square_height - 1, x + square_width - 1, y + 1
dc.pen = Wx::TRANSPARENT_PEN
dc.brush = Wx::Brush.new colors[shape]
dc.draw_rectangle x + 1, y + 1,
square_width - 2, square_height - 2
end
def on_paint(e)
paint do |dc|
size = get_client_size
board_top = size.height - BoardHeight * square_height
(0...BoardHeight).each do |i|
(0...BoardWidth).each do |j|
shape = shape_at j, BoardHeight - i - 1
if shape && shape != 0
draw_square dc,
0 + j * square_width,
board_top + i * square_height, shape
end
end
end
end
end
# sütun, satır
def shape_at(col, row)
return @board[row * BoardWidth + col]
end
def square_height
return client_size.height / BoardHeight
end
def square_width
return client_size.width / BoardWidth
end
end
Wx::App.run {
Tetris.new(nil).show
}
ve çıktısı

Şekiller sınıfı
Boyama işi bitti, şimdi şekillerimizi bir nesne olarak üreteceğiz ve yukarıdan aşağı kayacaklar. Bunun için ilk önce şekillerimizi ve davranışlarını belirleyeceğimiz bir Shape sınıfı tanımlayacağız. Önce bütün kodu vereceğim , sonra adım adım açıklamaları yaparız.
class Shape
@@coords_table = [
[[0, 0], [0, 0], [0, 0], [0, 0]],
[[0, -1], [0, 0], [1, 0], [1, 1]],
[[0, -1], [0, 0], [-1, 0], [-1, 1]],
[[0, -1], [0, 0], [0, 1], [0, 2]],
[[-1, 0], [0, 0], [1, 0], [0, 1]],
[[0, 0], [1, 0], [0, 1], [1, 1]],
[[-1, -1], [0, -1], [0, 0], [0, 1]],
[[1, -1], [0, -1], [0, 0], [0, 1]]
]
def initialize
@coords = [[0, 0], [0, 0], [0, 0], [0, 0]]
@piece_shape = 0
set_shape(0)
end
def shape
@piece_shape
end
def set_shape(shape)
table = @@coords_table[shape]
(0...4).each do |i|
(0...2).each do |j|
@coords[i][j] = table[i][j]
end
end
@piece_shape = shape
end
def set_random_shape
set_shape rand(1..7)
end
def x(index)
@coords[index][0]
end
def y(index)
@coords[index][1]
end
def setX(index, x)
@coords[index][0] = x
end
def setY(index, y)
@coords[index][1] = y
end
def minX
m = @coords[0][0]
(0...4).each do |i|
m = [m, @coords[i][0]].min
end
return m
end
def maxX
m = @coords[0][0]
(0...4).each do |i|
m = [m, @coords[i][0]].max
end
return m
end
def minY
m = @coords[0][1]
(0...4).each do |i|
m = [m, @coords[i][1]].min
end
return m
end
def maxY
m = @coords[0][1]
(0...4).each do |i|
m = [m, @coords[i][1]].max
end
return m
end
def rotated_left
if @piece_shape == 5 # kare şekil
return self
end
result = Shape.new
result.set_shape @piece_shape
(0...4).each do |i|
result.setX(i, y(i))
result.setY(i, -x(i))
end
return result
end
def rotated_right
if @piece_shape == 5 # kare şekil
return self
end
result = Shape.new
result.set_shape @piece_shape
(0...4).each do |i|
result.setX(i, -y(i))
result.setY(i, x(i))
end
return result
end
end
Burada @@coords_table değişkeni içinde taslağımızda bulunan tüm şekillerin 4 hücreden oluşan koordinatları bulunuyor. Taslağımıza karşı gelen değerleri bir inceleyelim.

Bu tablo şekillerin ilk defa ekrana gelişini gösteriyor, daha sonra döndürdükçe şekle ait bu dörtlü koordinatları değiştirdiğimizde dönüşleri gerçekleştireceğiz.
def initialize
@coords = [[0, 0], [0, 0], [0, 0], [0, 0]]
@piece_shape = 0
set_shape(0)
end
@coords oluşum değişkeninde o şeklin koordinatları saklanacak, şekli döndürdükçe mesela, bo array elemanlarını değiştirerek dönmesini sağlayacağız. @piece_shape oluşum değişkeninde ise o şeklin tasarımda karşı gelen index sayısı var.
def set_shape(shape)
table = @@coords_table[shape]
(0...4).each do |i|
(0...2).each do |j|
@coords[i][j] = table[i][j]
end
end
@piece_shape = shape
end
set_shape metodu ise bir şekle ilk değerlerini veriyor. @coords ve @piece_shape oluşum değişkenlerinin değerlerini istenen şekle göre veriyor. Aslında buna bakılırsa initialize metodundaki ilk iki satıra gerek kalmıyor gibi. Ama en azından @coords array'i için ilk değeri vermek gerekli.
def shape
@piece_shape
end
shape metodu dışarıdan @piece_shape oluşum değişkenini okumak için kullanılıyor.
def set_random_shape
set_shape rand(1..7)
end
set_random_shape metodu rastgele 7 şekilden birini seçmek için kullanılıyor.
def x(index)
@coords[index][0]
end
def y(index)
@coords[index][1]
end
Bu iki metod nesne koordinatlarının index'inci hücresinin x ve y değerlerini veriyor. Her hücrenin pozisyonunu okurken kullanacağız.
def setX(index, x)
@coords[index][0] = x
end
def setY(index, y)
@coords[index][1] = y
end
Bu iki metod da nesne koordinatlarının index'inci hücresinin x ve y değerlerini değiştiriyor. Hücreleri kaydırırken kullanacağız.
def minX
m = @coords[0][0]
(0...4).each do |i|
m = [m, @coords[i][0]].min
end
return m
end
def maxX
m = @coords[0][0]
(0...4).each do |i|
m = [m, @coords[i][0]].max
end
return m
end
minX değeri şeklin hücrelerinin en küçük x değerine sahip olan hücresinin x değeri, sol kenara dayandı mı, bu metodla kontrol edeceğiz.
maxX değeri şeklin hücrelerinin en büyük x değerine sahip olan hücresinin x değeri, sağ kenara dayandı mı, bu metodla kontrol edeceğiz.
def minY
m = @coords[0][1]
(0...4).each do |i|
m = [m, @coords[i][1]].min
end
return m
end
def maxY
m = @coords[0][1]
(0...4).each do |i|
m = [m, @coords[i][1]].max
end
return m
end
minY ve maxY de şeklin en alt ve en üst hücrelerinin y pozisyonlarını okumak için kullanılıyor.
def rotated_left
if @piece_shape == 5 # kare şekil
return self
end
result = Shape.new
result.set_shape @piece_shape
(0...4).each do |i|
result.setX(i, y(i))
result.setY(i, -x(i))
end
return result
end
Bu şekli sola döndürme rutini, daha doğrusu şeklin orijinalinin sola dönmüş halini veren rutin , şekil kare şekilse sorun yok dönmüşü de aynı olur. Diğer şekiller içim önce şeklin bir kopyasını oluşturuyoruz. Sonra her hücre için x yerine y değerini, y yerine de -x değerini koyuyoruz. Mesela 1 nolu şekil
[[0, -1], [0, 0], [1, 0], [1, 1]]
iken (yani dikey Z)
[[-1, 0], [0, 0], [0, -1], [1, -1]]
oluyor (yani yatay Z)
rotated_right metodu da tam tersi yani orjinal şeklin sağa dönmüş halini şekil nesnesi olarak bize verecektir.
Şimdi oyun tahtamızın (Board nesnesi) init_board metoduna dönelim ve birkaç ilave yapalım.
def init_board
@timer = Wx::Timer.new self, ID_TIMER
@cur_piece = Shape.new
@next_piece = Shape.new
@curX = 0
@curY = 0
@num_lines_removed = 0
@board = []
@is_started = false
@is_paused = false
evt_paint :on_paint
end
@cur_piece şu anda (oyun çalışırkenki aktif anda) tahtada hareket halinde olan şekil olacak, şimdilik boş bir şekil olarak başlattık. @next_piece ise sırada bir sonra gelecek olan şekil, onu da boş bir şekil nesnesi olarak başlattık. @curX ve @curY ise aktif şeklin referans alacağı pozisyon, yani tablada hareket eden şekil çizilirken @coords değerleri bunlara eklenecek ve hücreleri çizilecek. @num_lines_removed ise oyun devam ederken tamamını doldurup silinmesini sağladığımız satır sayısı, bunu skor olarak kullanıyoruz. @is_started ve @is_paused değişkenleri ise oyun çalışırken kullandığımız iki değişken. Bir de @board değişkeninde örnek olarak girdiğimiz hücre renk kodlarını kaldırdık.
Sıra geldi oyuna start vermeye bu amaçla Board nesnemize start adında bir metod ekleyeceğiz. Öncelikle ana pencere rutinimize bu start metodunu çağıran bir satır ekleyelim.
def init_UI
set_title "Tetris"
centre
@statusbar = create_status_bar
@statusbar.set_status_text '0'
@board = Board.new self
@board.set_focus
@board.start
end
Gelelim Board sınıfı start metoduna.
def start
if @is_paused
return
end
@is_started = true
@num_lines_removed = 0
clear_board
new_piece
@timer.start Speed
end
clear_board metodu oyun tablasını temizliyor.
def clear_board
(0...BoardHeight * BoardWidth).each do |i|
@board.append 0
end
end
Sadece şu anda boş olan @board array içini görünmeyen hücre renk kodu olan sıfır ile dolduruyoruz. Bundan sonra on_paint olay metodu tetiklenirse tüm tabla boş çizilecektir , daha doğrusu hücre renk kodu sıfır olduğu için hücreler boyanmayacaktır.
new_piece metodu ise yeni bir şekli üretiyor.
def new_piece
@cur_piece = @next_piece.dup
statusbar = parent.status_bar
@next_piece.set_random_shape
@curX = BoardWidth / 2 + 1
@curY = BoardHeight - 1 + @cur_piece.minY
if not try_move(@cur_piece, @curX, @curY)
@timer.stop
@is_started = false
statusbar.status_text = 'Game over'
end
end
Bu new_piece metodunu oyun ilerlerken tablaya yeni bir parça eklerken çağırıp duracağız. Öncelikle sonraki parçayı kopyalayarak şimdiki parça yapıyor. Sonraki parçaya rastgele bir şekil seçtirerek hazırlıyor. Start vermeden önce de bu sonraki şekle rastgele bir seçim yapsak iyi olacak, tablayı kurarken boş şekil üretmiştik.
def init_board
@timer = Wx::Timer.new self, ID_TIMER
@cur_piece = Shape.new
@next_piece = Shape.new
@next_piece.set_random_shape
@curX = 0
.....
@curX ve @curY yani hareket etmekte olan şeklin koordinat merkezini tablanın üst ortasına getiriyoruz, bunu yaparken şeklim en altta kalan sırası tablanın en üst satırına gelecek şekilde ayarlıyoruz.
try_move metodu verilen parçayı verilen koordinata götürmeye uğraşacak ve eğer başarılı olursa parçayı o koordinata kaydıracak ve true değer dönecek. Başarısız olursa , yani sağa sola dayanırsa ya da aşağı inerken başka parçaya ya da zemine dayandıysa parça verilen koordinata kaydırılamayarak false değer döner. Burada yeni parça üretirken eğer tablaya koyamıyorsa demek ki tabla doludur ve oyun bitmiş demektir.
def try_move(new_piece, newX, newY)
(0...4).each do |i|
x = newX + new_piece.x(i)
y = newY - new_piece.y(i)
if x < 0 or x >= BoardWidth or y < 0 or y >= BoardHeight
return false
end
if shape_at(x, y) != 0
return false
end
end
@cur_piece = new_piece
@curX = newX
@curY = newY
refresh
return true
end
Şeklin her bir hücresinin hedefte kayacağı yere bakılıyor, eğer orası tablanın sınırları dışındaysa ya da orada başka bir renk kodu varsa false değer döner. Sorun yoksa @cur_piece, @curX ve @curY değerleri yenilenip on_paint olay metodu tetiklenip tabla boyansın diye refresh metodu çağrılıyor.
Parça ekrana eklendikten sonra aşağı kaymaya devam etmesi için @timer için bir olay işleme rutini eklemeliyiz. Öncelikle init_board metoduna olay işleme metodu çağrısını ekleyelim.
def init_board
@timer = Wx::Timer.new self, ID_TIMER
@cur_piece = Shape.new
@next_piece = Shape.new
@next_piece.set_random_shape
@curX = 0
@curY = 0
@num_lines_removed = 0
@board = []
@is_started = false
@is_paused = false
evt_paint :on_paint
evt_timer ID_TIMER, :on_timer
end
Sonra da on_timer olay işleme metodunu ekleyelim.
def on_timer(event)
if event.id == ID_TIMER
if @is_waiting_after_line
@is_waiting_after_line = false
new_piece
else
one_line_down
end
else
event.skip
end
end
@is_waiting_after_line oluşum değişkeninde önceki parça bitmiş sonrakine geçilmesi gerektiği bilgisi olacak. Öncelikle Board nesnesi üretirken bu değişkenin ilk değerini vermeliyiz.
def init_board
@timer = Wx::Timer.new self, ID_TIMER
@is_waiting_after_line = false
@cur_piece = Shape.new
....
end
one_line_down metodu aktif parçayı bir hücre aşağı kaydıracak.
def one_line_down
if not try_move @cur_piece, @curX, @curY - 1
piece_dropped
end
end
Öncelikle aktif parçanın y ekseni değerini bir azaltmaya çalışacak, başarılı olursa sorun yok parça aşağı kaymış olacak. Kaydırma başarısız olursa parça bir yere dayanmış demektir, yani aşağı gidecek yeri kalmamıştır ve piece_dropped metodu çağrılarak hem o parça @board array'inde yerleştirilecek , hem de yeni parçaya geçilecek.
Bu arada programı şu anda çalıştırırsak parçanın en aşağıya düşene kadar hareketini görmemiz gerekiyor, ama on_paint olay işleme metodunda bu parça için bir ilave yapmadığımız için görünmez, ve bir süre çalıştıktan sonra program piece_dropped metodunu bulamadığı için hata verecektir. Yani aslında parça kayıyor, en alta geliyor ama göremiyoruz. on_paint metoduna geri dönüp ilavemizi yapalım.
def on_paint(e)
paint do |dc|
size = get_client_size
board_top = size.height - BoardHeight * square_height
(0...BoardHeight).each do |i|
(0...BoardWidth).each do |j|
shape = shape_at j, BoardHeight - i - 1
if shape && shape != 0
draw_square dc,
0 + j * square_width,
board_top + i * square_height, shape
end
end
end
if @cur_piece.shape != 0
(0...4).each do |i|
x = @curX + @cur_piece.x(i)
y = @curY - @cur_piece.y(i)
draw_square dc, 0 + x * square_width,
board_top + (BoardHeight - y - 1) * square_height,
@cur_piece.shape
end
end
end
end
Eğer aktif şekil boş şekil değilse her hücresini aktif koordinata göre boyuyoruz. Şimdi çalıştırırsak parçanın hareket edip aşağı kenara kadar ulaştıktan sonra piece_dropped metodu bulunamadı der.
Gelelim metoda.
def piece_dropped
(0...4).each do |i|
x = @curX + @cur_piece.x(i)
y = @curY - @cur_piece.y(i)
set_shape_at x, y, @cur_piece.shape
end
remove_full_lines
if not @is_waiting_after_line
new_piece
end
end
Burada set_shape_at metodu @board array'inde verilen hücreye o şekil kodunu koyar.
def set_shape_at(x, y, shape)
@board[(y * BoardWidth) + x] = shape
end
remove_full_lines metodu hareketli parça en alta indiğinde oluşmuş olan bütünü dolu satırları silecek.
def remove_full_lines
num_full_lines = 0
statusbar = parent.status_bar
rows_to_remove = []
(0...BoardHeight).each do |i|
n = 0
(0...BoardWidth).each do |j|
if not shape_at(j, i) == 0
n = n + 1
end
if n == 10
rows_to_remove.append(i)
end
end
end
rows_to_remove.reverse!
rows_to_remove.each do |m|
(m...BoardHeight).each do |k|
(0...BoardWidth).each do |l|
set_shape_at l, k, shape_at(l, k + 1)
end
end
num_full_lines = num_full_lines + rows_to_remove.length
if num_full_lines > 0
@num_lines_removed = @num_lines_removed + num_full_lines
statusbar.status_text = @num_lines_removed.to_s
@is_waiting_after_line = true
refresh
end
end
end
Bu metod biraz uzun ama yaptığı iş karmaşık değil. Önce rows_to_remove adında bir array içinde silinecek bütünü dolu satırların numaralarını topluyoruz. Sonra her bir bütünü dolu satırın üzerindeki tüm satırları bir satır aşağı kaydırarak bütünü dolu o satırı yok ediyoruz. num_full_lines değişkeninde silinecek kalan satırların sayısı var. @num_lines_removed değişkeninde ise şimdiye kadar silinmiş olan toplam satır sayısı var ve bunu skor değeri olarak durum çubuğuna yazıyoruz. Çoklu satır silmelerinde de skor katlayarak artıyor.
Programı çalıştırınca bir şey dikkatimi çekti, şekillerin renk kodlarında bir kayma var, kendi renginde değil önceki şeklin renginde çıkıyor. Demek yeni şekil belirlenirken bir eksiğimiz var. Olay new_piece metodu en başında kopuyor, dup ile nesneyi kopyalıyoruz da o nesnenin @coords değerleri kopyalanmıyor.
def new_piece
@cur_piece = @next_piece.dup
yerine
def new_piece
@cur_piece.set_shape @next_piece.shape
....
yaparsak daha iyi , bu nesne kopyalamalar her zaman dert çıkarır zaten.
Bir de Game Over yazarken en son skoru da yazsak fena olmayacak.
def new_piece
....
statusbar.status_text = "Score: #{@num_lines_removed} - Game over"
end
end
Bu noktada programın son halini bir versem iyi olacak
tetris.rb
require "wx"
class Tetris < Wx::Frame
def initialize(parent)
super(parent, size: [180,380],
style: Wx::DEFAULT_FRAME_STYLE ^ Wx::RESIZE_BORDER ^ Wx::MAXIMIZE_BOX)
init_UI
end
def init_UI
set_title "Tetris"
centre
@statusbar = create_status_bar
@statusbar.set_status_text '0'
@board = Board.new self
@board.set_focus
@board.start
end
end
class Board < Wx::Panel
BoardWidth = 10
BoardHeight = 22
Speed = 300
ID_TIMER = 1
def initialize(*args)
super(*args)
init_board
end
def init_board
@timer = Wx::Timer.new self, ID_TIMER
@is_waiting_after_line = false
@cur_piece = Shape.new
@next_piece = Shape.new
@next_piece.set_random_shape
@curX = 0
@curY = 0
@num_lines_removed = 0
@board = []
@is_started = false
@is_paused = false
evt_paint :on_paint
evt_timer ID_TIMER, :on_timer
end
def clear_board
(0...BoardHeight * BoardWidth).each do |i|
@board.append 0
end
end
def draw_square(dc, x, y, shape)
colors = ['#000000', '#CC6666', '#66CC66', '#6666CC',
'#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
light = ['#000000', '#F89FAB', '#79FC79', '#7979FC',
'#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80',
'#80803B', '#803B80', '#3B8080', '#806200']
pen = Wx::Pen.new light[shape]
pen.cap = Wx::CAP_PROJECTING
dc.pen = pen
dc.draw_line x, y + square_height - 1, x, y
dc.draw_line x, y, x + square_width - 1, y
darkpen = Wx::Pen.new dark[shape]
darkpen.cap = Wx::CAP_PROJECTING
dc.pen = darkpen
dc.draw_line x + 1, y + square_height - 1,
x + square_width - 1, y + square_height - 1
dc.draw_line x + square_width - 1,
y + square_height - 1, x + square_width - 1, y + 1
dc.pen = Wx::TRANSPARENT_PEN
dc.brush = Wx::Brush.new colors[shape]
dc.draw_rectangle x + 1, y + 1,
square_width - 2, square_height - 2
end
def new_piece
@cur_piece.set_shape @next_piece.shape
statusbar = parent.status_bar
@next_piece.set_random_shape
@curX = BoardWidth / 2 + 1
@curY = BoardHeight - 1 + @cur_piece.minY
if not try_move(@cur_piece, @curX, @curY)
@cur_piece.set_shape 0
@timer.stop
@is_started = false
statusbar.status_text = "Score: #{@num_lines_removed} - Game over"
end
end
def on_paint(e)
paint do |dc|
size = get_client_size
board_top = size.height - BoardHeight * square_height
(0...BoardHeight).each do |i|
(0...BoardWidth).each do |j|
shape = shape_at j, BoardHeight - i - 1
if shape && shape != 0
draw_square dc,
0 + j * square_width,
board_top + i * square_height, shape
end
end
end
if @cur_piece.shape != 0
(0...4).each do |i|
x = @curX + @cur_piece.x(i)
y = @curY - @cur_piece.y(i)
draw_square dc, 0 + x * square_width,
board_top + (BoardHeight - y - 1) * square_height,
@cur_piece.shape
end
end
end
end
def on_timer(event)
if event.id == ID_TIMER
if @is_waiting_after_line
@is_waiting_after_line = false
new_piece
else
one_line_down
end
else
event.skip
end
end
def one_line_down
if not try_move @cur_piece, @curX, @curY - 1
piece_dropped
end
end
def piece_dropped
(0...4).each do |i|
x = @curX + @cur_piece.x(i)
y = @curY - @cur_piece.y(i)
set_shape_at x, y, @cur_piece.shape
end
remove_full_lines
if not @is_waiting_after_line
new_piece
end
end
def remove_full_lines
num_full_lines = 0
statusbar = parent.status_bar
rows_to_remove = []
(0...BoardHeight).each do |i|
n = 0
(0...BoardWidth).each do |j|
if not shape_at(j, i) == 0
n = n + 1
end
if n == 10
rows_to_remove.append(i)
end
end
end
rows_to_remove.reverse!
rows_to_remove.each do |m|
(m...BoardHeight).each do |k|
(0...BoardWidth).each do |l|
set_shape_at l, k, shape_at(l, k + 1)
end
end
num_full_lines = num_full_lines + rows_to_remove.length
if num_full_lines > 0
@num_lines_removed = @num_lines_removed + num_full_lines
statusbar.status_text = @num_lines_removed.to_s
@is_waiting_after_line = true
@cur_piece.set_shape 0
refresh
end
end
end
def set_shape_at(x, y, shape)
@board[(y * BoardWidth) + x] = shape
end
# sütun, satır
def shape_at(col, row)
return @board[row * BoardWidth + col]
end
def square_height
return client_size.height / BoardHeight
end
def square_width
return client_size.width / BoardWidth
end
def start
if @is_paused
return
end
@is_started = true
#@is_waiting_after_line = false
@num_lines_removed = 0
clear_board
new_piece
@timer.start Speed
end
def try_move(new_piece, newX, newY)
(0...4).each do |i|
x = newX + new_piece.x(i)
y = newY - new_piece.y(i)
if x < 0 or x >= BoardWidth or y < 0 or y >= BoardHeight
return false
end
if shape_at(x, y) != 0
return false
end
end
@cur_piece = new_piece
@curX = newX
@curY = newY
refresh
return true
end
end
class Shape
@@coords_table = [
[[0, 0], [0, 0], [0, 0], [0, 0]],
[[0, -1], [0, 0], [1, 0], [1, 1]],
[[0, -1], [0, 0], [-1, 0], [-1, 1]],
[[0, -1], [0, 0], [0, 1], [0, 2]],
[[-1, 0], [0, 0], [1, 0], [0, 1]],
[[0, 0], [1, 0], [0, 1], [1, 1]],
[[-1, -1], [0, -1], [0, 0], [0, 1]],
[[1, -1], [0, -1], [0, 0], [0, 1]]
]
def initialize
@coords = [[0, 0], [0, 0], [0, 0], [0, 0]]
@piece_shape = 0
set_shape(0)
end
def shape
@piece_shape
end
def set_shape(shape)
table = @@coords_table[shape]
(0...4).each do |i|
(0...2).each do |j|
@coords[i][j] = table[i][j]
end
end
@piece_shape = shape
end
def set_random_shape
set_shape rand(1..7)
end
def x(index)
@coords[index][0]
end
def y(index)
@coords[index][1]
end
def setX(index, x)
@coords[index][0] = x
end
def setY(index, y)
@coords[index][1] = y
end
def minX
m = @coords[0][0]
(0...4).each do |i|
m = [m, @coords[i][0]].min
end
return m
end
def maxX
m = @coords[0][0]
(0...4).each do |i|
m = [m, @coords[i][0]].max
end
return m
end
def minY
m = @coords[0][1]
(0...4).each do |i|
m = [m, @coords[i][1]].min
end
return m
end
def maxY
m = @coords[0][1]
(0...4).each do |i|
m = [m, @coords[i][1]].max
end
return m
end
def rotated_left
if @piece_shape == 5 # kare şekil
return self
end
result = Shape.new
result.set_shape @piece_shape
(0...4).each do |i|
result.setX(i, y(i))
result.setY(i, -x(i))
end
return result
end
def rotated_right
if @piece_shape == 5 # kare şekil
return self
end
result = Shape.new
result.set_shape @piece_shape
(0...4).each do |i|
result.setX(i, -y(i))
result.setY(i, x(i))
end
return result
end
end
Wx::App.run {
Tetris.new(nil).show
}
Tuş olayları
Parçalar düşmeye başladı, şimdi tuşlara basınca parçalara olacak işlemlere geldik. Öncelikle Olay metodunu çağırmakla başlayalım.
def init_board
.....
evt_paint :on_paint
evt_timer ID_TIMER, :on_timer
evt_char_hook :on_key_down
end
Ve on_key_down olay işleme metodu tanımı.
def on_key_down(event)
if not @is_started or @cur_piece.shape == 0
event.skip
return
end
keycode = event.key_code
if keycode == 'P'.ord or keycode == 'p'.ord
pause
return
end
if @is_paused
return
elsif keycode == Wx::K_LEFT
try_move @cur_piece, @curX - 1, @curY
elsif keycode == Wx::K_RIGHT
try_move @cur_piece, @curX + 1, @curY
elsif keycode == Wx::K_DOWN
try_move @cur_piece.rotated_right, @curX, @curY
elsif keycode == Wx::K_UP
try_move @cur_piece.rotated_left, @curX, @curY
elsif keycode == Wx::K_SPACE
drop_down
elsif keycode == 'D'.ord or keycode == 'd'.ord
one_line_down
else
event.skip
end
end
Burada ilk eklememiz gereken pause metodu olacak.
def pause
if not @is_started
return
end
@is_paused = not @is_paused
statusbar = self.parent.status_bar
if @is_paused
@timer.stop
statusbar.status_text = 'paused'
else
@timer.start Speed
statusbar.status_text = @num_lines_removed.to_s
end
refresh
end
P ya da p harfi gelince çağrılan pause metodumuz @timer zamanlayıcısını durdurarak hareketi engelliyor. Bir daha P ya da p basılınca tekrar devam ediyor.
Sol ve sağ ok tuşları parçayı sola ya da sağa kaydırıyor, yukarı ve aşağı ok tuşları ise döndürme yapıyor, metod çağrıları mevcut olduğu için şu anda programı çalıştırsak sağlıklı iş yaparlar.
Aralık çubuğuna basınca çağrılan drop_down metodu parçayı düşürecek.
def drop_down
newY = @curY
while newY > 0
if not try_move(@cur_piece, @curX, newY - 1)
break
end
newY -= 1
end
piece_dropped
end
Parçayı aşağıda bir şeye dayanana kadar zamanlayıcıyı beklemeden kaydırıyor ve piece_dropped metodunu çağırarak yeni parçaya geçişi sağlıyor.
Burada denem yapınca bir şey dikkatimi çekti, bir bütün satır oluşturunca Game Over oluyor. Sebebi remove_full_lines metodunda satırları aşağı kaydırırken en üstten nil değeri girmesi, bunu engellemek için en üstte nil gelince şekil kodu sıfır olsun diye
def remove_full_lines
....
rows_to_remove.each do |m|
(m...BoardHeight).each do |k|
(0...BoardWidth).each do |l|
set_shape_at l, k, shape_at(l, k + 1)||0
olması gerekir.
Şimdi oyunumuz çalışıyor fakat boyama işleri çok vakit aldığı için biraz tekleye tekleye gidiyor. İsterseniz draw_square içinde kenar çizgileri çizen satırları yorum içine alarak hızlandırma yapabilirsiniz.
size son halini bir daha vereyim , bu yazıyı da burada bitireyim.
tetris.rb
require "wx"
class Tetris < Wx::Frame
def initialize(parent)
super(parent, size: [180,380],
style: Wx::DEFAULT_FRAME_STYLE ^ Wx::RESIZE_BORDER ^ Wx::MAXIMIZE_BOX)
init_UI
end
def init_UI
set_title "Tetris"
centre
@statusbar = create_status_bar
@statusbar.set_status_text '0'
@board = Board.new self
@board.set_focus
@board.start
end
end
class Board < Wx::Panel
BoardWidth = 10
BoardHeight = 22
Speed = 300
ID_TIMER = 1
def initialize(*args)
super(*args)
init_board
end
def init_board
@timer = Wx::Timer.new self, ID_TIMER
@is_waiting_after_line = false
@cur_piece = Shape.new
@next_piece = Shape.new
@next_piece.set_random_shape
@curX = 0
@curY = 0
@num_lines_removed = 0
@board = []
@is_started = false
@is_paused = false
evt_paint :on_paint
evt_timer ID_TIMER, :on_timer
evt_char_hook :on_key_down
end
def clear_board
(0...BoardHeight * BoardWidth).each do |i|
@board.append 0
end
end
def draw_square(dc, x, y, shape)
colors = ['#000000', '#CC6666', '#66CC66', '#6666CC',
'#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
light = ['#000000', '#F89FAB', '#79FC79', '#7979FC',
'#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80',
'#80803B', '#803B80', '#3B8080', '#806200']
=begin
pen = Wx::Pen.new light[shape]
pen.cap = Wx::CAP_PROJECTING
dc.pen = pen
dc.draw_line x, y + square_height - 1, x, y
dc.draw_line x, y, x + square_width - 1, y
darkpen = Wx::Pen.new dark[shape]
darkpen.cap = Wx::CAP_PROJECTING
dc.pen = darkpen
dc.draw_line x + 1, y + square_height - 1,
x + square_width - 1, y + square_height - 1
dc.draw_line x + square_width - 1,
y + square_height - 1, x + square_width - 1, y + 1
=end
dc.pen = Wx::TRANSPARENT_PEN
dc.brush = Wx::Brush.new colors[shape]
dc.draw_rectangle x + 1, y + 1,
square_width - 2, square_height - 2
end
def drop_down
newY = @curY
while newY > 0
if not try_move(@cur_piece, @curX, newY - 1)
break
end
newY -= 1
end
piece_dropped
end
def new_piece
@cur_piece.set_shape @next_piece.shape
statusbar = parent.status_bar
@next_piece.set_random_shape
@curX = BoardWidth / 2 + 1
@curY = BoardHeight - 1 + @cur_piece.minY
if not try_move(@cur_piece, @curX, @curY)
@cur_piece.set_shape 0
@timer.stop
@is_started = false
statusbar.status_text = "Score: #{@num_lines_removed} - Game over"
end
end
def on_key_down(event)
if not @is_started or @cur_piece.shape == 0
event.skip
return
end
keycode = event.key_code
if keycode == 'P'.ord or keycode == 'p'.ord
pause
return
end
if @is_paused
return
elsif keycode == Wx::K_LEFT
try_move @cur_piece, @curX - 1, @curY
elsif keycode == Wx::K_RIGHT
try_move @cur_piece, @curX + 1, @curY
elsif keycode == Wx::K_DOWN
try_move @cur_piece.rotated_right, @curX, @curY
elsif keycode == Wx::K_UP
try_move @cur_piece.rotated_left, @curX, @curY
elsif keycode == Wx::K_SPACE
drop_down
elsif keycode == 'D'.ord or keycode == 'd'.ord
one_line_down
else
event.skip
end
end
def on_paint(e)
paint do |dc|
size = get_client_size
board_top = size.height - BoardHeight * square_height
(0...BoardHeight).each do |i|
(0...BoardWidth).each do |j|
shape = shape_at j, BoardHeight - i - 1
if shape && shape != 0
draw_square dc,
0 + j * square_width,
board_top + i * square_height, shape
end
end
end
if @cur_piece.shape != 0
(0...4).each do |i|
x = @curX + @cur_piece.x(i)
y = @curY - @cur_piece.y(i)
draw_square dc, 0 + x * square_width,
board_top + (BoardHeight - y - 1) * square_height,
@cur_piece.shape
end
end
end
end
def on_timer(event)
if event.id == ID_TIMER
if @is_waiting_after_line
@is_waiting_after_line = false
new_piece
else
one_line_down
end
else
event.skip
end
end
def one_line_down
if not try_move @cur_piece, @curX, @curY - 1
piece_dropped
end
end
def pause
if not @is_started
return
end
@is_paused = not @is_paused
statusbar = self.parent.status_bar
if @is_paused
@timer.stop
statusbar.status_text = 'paused'
else
@timer.start Speed
statusbar.status_text = @num_lines_removed.to_s
end
refresh
end
def piece_dropped
(0...4).each do |i|
x = @curX + @cur_piece.x(i)
y = @curY - @cur_piece.y(i)
set_shape_at x, y, @cur_piece.shape
end
remove_full_lines
if not @is_waiting_after_line
new_piece
end
end
def remove_full_lines
num_full_lines = 0
statusbar = parent.status_bar
rows_to_remove = []
(0...BoardHeight).each do |i|
n = 0
(0...BoardWidth).each do |j|
if not shape_at(j, i) == 0
n = n + 1
end
if n == 10
rows_to_remove.append(i)
end
end
end
rows_to_remove.reverse!
rows_to_remove.each do |m|
(m...BoardHeight).each do |k|
(0...BoardWidth).each do |l|
set_shape_at l, k, shape_at(l, k + 1)||0
end
end
num_full_lines = num_full_lines + rows_to_remove.length
if num_full_lines > 0
@num_lines_removed = @num_lines_removed + num_full_lines
statusbar.status_text = @num_lines_removed.to_s
@is_waiting_after_line = true
@cur_piece.set_shape 0
refresh
end
end
end
def set_shape_at(x, y, shape)
@board[(y * BoardWidth) + x] = shape
end
# sütun, satır
def shape_at(col, row)
return @board[row * BoardWidth + col]
end
def square_height
return client_size.height / BoardHeight
end
def square_width
return client_size.width / BoardWidth
end
def start
if @is_paused
return
end
@is_started = true
#@is_waiting_after_line = false
@num_lines_removed = 0
clear_board
new_piece
@timer.start Speed
end
def try_move(new_piece, newX, newY)
(0...4).each do |i|
x = newX + new_piece.x(i)
y = newY - new_piece.y(i)
if x < 0 or x >= BoardWidth or y < 0 or y >= BoardHeight
return false
end
if shape_at(x, y) != 0
return false
end
end
@cur_piece = new_piece
@curX = newX
@curY = newY
refresh
return true
end
end
class Shape
@@coords_table = [
[[0, 0], [0, 0], [0, 0], [0, 0]],
[[0, -1], [0, 0], [1, 0], [1, 1]],
[[0, -1], [0, 0], [-1, 0], [-1, 1]],
[[0, -1], [0, 0], [0, 1], [0, 2]],
[[-1, 0], [0, 0], [1, 0], [0, 1]],
[[0, 0], [1, 0], [0, 1], [1, 1]],
[[-1, -1], [0, -1], [0, 0], [0, 1]],
[[1, -1], [0, -1], [0, 0], [0, 1]]
]
def initialize
@coords = [[0, 0], [0, 0], [0, 0], [0, 0]]
@piece_shape = 0
set_shape(0)
end
def shape
@piece_shape
end
def set_shape(shape)
table = @@coords_table[shape]
(0...4).each do |i|
(0...2).each do |j|
@coords[i][j] = table[i][j]
end
end
@piece_shape = shape
end
def set_random_shape
set_shape rand(1..7)
end
def x(index)
@coords[index][0]
end
def y(index)
@coords[index][1]
end
def setX(index, x)
@coords[index][0] = x
end
def setY(index, y)
@coords[index][1] = y
end
def minX
m = @coords[0][0]
(0...4).each do |i|
m = [m, @coords[i][0]].min
end
return m
end
def maxX
m = @coords[0][0]
(0...4).each do |i|
m = [m, @coords[i][0]].max
end
return m
end
def minY
m = @coords[0][1]
(0...4).each do |i|
m = [m, @coords[i][1]].min
end
return m
end
def maxY
m = @coords[0][1]
(0...4).each do |i|
m = [m, @coords[i][1]].max
end
return m
end
def rotated_left
if @piece_shape == 5 # kare şekil
return self
end
result = Shape.new
result.set_shape @piece_shape
(0...4).each do |i|
result.setX(i, y(i))
result.setY(i, -x(i))
end
return result
end
def rotated_right
if @piece_shape == 5 # kare şekil
return self
end
result = Shape.new
result.set_shape @piece_shape
(0...4).each do |i|
result.setX(i, -y(i))
result.setY(i, x(i))
end
return result
end
end
Wx::App.run {
Tetris.new(nil).show
}
Yani diyeceğim odur ki Ruby ile masaüstü program yazmak isterseniz WxRuby3 ile kolayca yapabilirsiniz. Kendinize iyi bakın, güzel günlerde görüşmek dileğiyle , şimdilik kalın sağlıcakla..
Hiç yorum yok:
Yorum Gönder