2018年12月25日火曜日

WxPythonのテトリスがあったのでコードを読んでみた。№6完成


参考サイト

テトリス
http://wxpython.at-ninja.jp/thetetrisgame.html

だいぶ複雑なコードなので順に読み解いていきます。
最初はウィンドウなどの表示のみのコード
№2ブロックも表示
№3ブロック落下
№4ブロック回転移動
№5底にブロックが接触したら、あたらしいブロックを発生し落下させる
№6完成
#********* のところは前回から付け加えた部分。


コード変更点


横一列にブロックが揃った所をけし、その上のブロック全てを一個下へ落とす操作を
removeFullLines にて実行します。 
removeFullLinesの numFullLines += len(rowsToRemove)より下の部分のすべての行
行頭に余分にタブがはいっているようです。
そのため、ステータスバーの数値が余分に増加していました。
それで該当する行のタブを削除しました。


動作確認のため setRandomShapeは以下のようにしてあります。
def setRandomShape(self):
print "setRandomShape "
#1~7までの乱数を発生
#self.setShape(random.randint(1, 7))
self.setShape(5)

squareHeight squareWidth は元コードどおり関数にしました。

またブロックは元コードどおり、色をつけました。

コード

E:\MyBackups\goolgedrive\myprg_main\python_my_prg\wxpython\game_tetori_retu_sorou.py

# coding: UTF-8

#参考サイト
#http://wxpython.at-ninja.jp/thetetrisgame.html
#テトリス

#底辺にブロックがついたら、次のブロックを発生させる
#横一列にブロックのセルが隙間なく並んだら、その列を消滅させる

#WxPython カーソルキーイベントを検知しないようなので
#別のキーイベントを検知するコードを入れる

#Shapeクラスでcoordsを決定してブロックの形状を決定します。
#それをOnPaintで実際の座標に変換して、drawSquareでブロックを
#描きます
#coordsでの形状表示が、OnPaintで実際の座標に変換される時
#y軸方向の表示が上下逆になります。

#横10 ブロックの座標で x = 0~9 
#縦22 ブロックの座標で y = 0~21
#で表現されている

#curX curY はブロックの基準となるブロック座標
#curX の初期値は6 ブロック形状座標0のセルがここにくる
#curY の初期値は 下の計算式とブロック形状によって
#yが21となるよう curYが決められる。
#これによりどのようなブロック形状座標であろうと、ブロックの 
#初期描画で描画表示ウィンドウの最上部と隙間なく、描画される

#curPiece はShapeのcoordsで与えられる、0を基準とした
#ブロックの形状を表すブロックのセルの位置座標
#(ブロック形状座標と呼ぼう)を示す
#x(i) y(i) はおのおの それの x y成分をしめす。
#x = self.curX + self.curPiece.x(i)
#y = self.curY - self.curPiece.y(i)
#x y はブロックのセル各々のブロック座標となる

import threading
import ctypes
import time
import wx
import random

#キーイベント取得のための定数
LEFT = 0x25
RIGHT = 0x27
DOWN = 0x28
UP   = 0x26
#下のようにかいたら思うような結果ならない
#直接16進数で書く必要がある
#UP   = hex(38)
#他のキーコードを調べるにはGetAsyncKeyStateで検索するといいかも

keycode = 0

class MyApp(wx.App):
 def __init__(self): 
  #エラーが出たらファイルに書き出す
  wx.App.__init__(self, redirect=True, filename="game_tetoris_log.txt")
  #wx.App.__init__(self, redirect=True )


class Tetris(wx.Frame):
 def __init__(self, parent, id, title):
  print "Tetris __init__",
  wx.Frame.__init__(self, parent, id, title, size=(180, 380))
  #ステータスバー作成
  self.statusbar = self.CreateStatusBar()
  self.statusbar.SetStatusText('0')
  #boardはBoardクラスにも使われているからややこしい
  self.board = Board(self)
  self.board.SetFocus()
  #初期値設定 タイマースタート
  self.board.start()
  self.Centre()
  self.Show(True)


class Board(wx.Panel):
 BoardWidth  = 10
 BoardHeight = 22
 Speed    = 300
 ID_TIMER = 1

 def __init__(self, parent):
  print "",
  print "Board __init__",
  wx.Panel.__init__(self, parent)
  #イベントをidで区別しているようだ
  # evet_id.py 参照
  self.timer     = wx.Timer(self, Board.ID_TIMER)
  self.curPiece    = Shape()#現在のブロックの初期設定
  self.nextPiece    = Shape()#次のブロックの初期設定

  #現在落下中のブロックを描画するときは、ブロックを self.curX
  #aafと self.curY の位置に描画します。 そして、座標テーブ
  #ルを参照し、4つの正方形全てを描画します。
  self.curX      = 0
  self.curY      = 0
  self.numLinesRemoved = 0
  self.board     = []#ブロックのブロックの位置

  self.isStarted = False
  self.isPaused  = False

  #再描画の時OnPaintを呼び出す
  self.Bind(wx.EVT_PAINT, self.OnPaint)
  self.Bind(wx.EVT_TIMER, self.OnTimer, id=Board.ID_TIMER)
  self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

  self.clearBoard()


 #ブロック座標にブロックのセルがはいっているかを調べる
 #shape = Tetrominoes.NoShape ならなにかのセルが存在する
 #例えばx,y=0ならboard[0] となる
 #x=0,y=1 なら board[10] となってうまくブロック座標のセルの
 #状態を示す事ができる
 def shapeAt(self, x, y):       
  print "shapeAt"
  return self.board[(y * Board.BoardWidth) + x]

 #ブロック座標にブロックのセルのインデックス番号を入れる 
 def setShapeAt(self, x, y, shape):       
  print "setShapeAt"
  #boardは要素数22*10 初期値全て0
  #以下の一行 BoardHeight ならわかるのだが
  self.board[(y * Board.BoardWidth) + x] = shape
  print "setShapeAt board", self.board


 # squareWidth = self.GetClientSize().GetWidth() / Board.BoardWidth 
 #として定数化しても良いように思うが、__init__の中では
 #GetClientSize()がうまく働かない。
 #またFrame のウィンドウサイズは(180, 380)だが、実際どれだけの大きさか
 #わからないので、こうやって関数にするのがベストか?
 def squareWidth(self):
  #print "squareWidth"
  #return 17
  #一度OnPaintを実行しないと正常にGetClientSizeが実行されないようだ
  #                            172       /         10        = 17
  #下のようにかいたら思うような結果ならない
  #下のようにかいたら思うような結果ならない
  #直接16進数で各必要がある
  #直接16進数で各必要がある
  return self.GetClientSize().GetWidth() / Board.BoardWidth

 #ウィンドウの中で描画できる範囲の高さを求め、その中に22個のブロックのセル
 #を置いた場合の一個のセルの高さを求めている?
 def squareHeight(self):
  #print "squareHeight"
  #return 15 
  #                            333        /   22   = 15
  return self.GetClientSize().GetHeight() / Board.BoardHeight

 def start(self):
  print "start",
  #if self.isPaused:#isPaused:なんかのフラグ 初期値False
  # return


  self.isStarted    = True  #isPausedがTrueならisStartedをTrueに
  self.isWaitingAfterLine = False #初期値False
  #self.numLinesRemoved = 0
 
  #__init__の中にもあるし二度手間では?
  self.clearBoard()
  self.newPiece()
  #300ミリ秒ごとにOnTimer()が実行される
  self.timer.Start(Board.Speed)      


 #pを押すことによりゲームが止まったりうごいたり
 def pause(self):                                          #*********
  print "pause"
  #isStartedがFalseになっているのは、初期状態しかないと思うに
  #わざわざ下のコードが必要な理由がわからん
  if not self.isStarted:
   return

  self.isPaused = not self.isPaused
  statusbar = self.GetParent().statusbar

  if self.isPaused:
   self.timer.Stop()
   statusbar.SetStatusText('paused')
  else:
   self.timer.Start(Board.Speed)
   statusbar.SetStatusText(str(self.numLinesRemoved))

  #RefreshによってOnPaintが呼ばれる
  self.Refresh()

 def clearBoard(self):
  print
  print "clearBoard"
  for i in range(Board.BoardHeight * Board.BoardWidth):
   self.board.append(Tetrominoes.NoShape)# ()のなかは0
  #boardがの要素が全部0になる


 def OnPaint(self, event):
  print "OnPaint",
  dc = wx.PaintDC(self)

  #ウィンドウ 'クライアント領域'のサイズをピクセル単位で返し
  #ます。クライアント領域は、タイトルバー、ボーダー、スクロー
  #ルバーなどを除いて、プログラマが描画できる領域です
  size = self.GetClientSize()

  #squareHeight:ブロックのミノ(セル)の高さ
  #squareHeight*BoardHeight(22)はGetHeightになる
  #(ウィンドウの大きさがかわらなければ) 

  #最初 
  #                            333        /   22   = 15 
  #return self.GetClientSize().GetHeight() / Board.BoardHeight
  #としてsquareHeight を決めたけれど ウィンドウにboardTopの分
  #だけ残ってしまうという事

  #  3     =        333       -          22       *     15 
  boardTop = size.GetHeight() - Board.BoardHeight * self.squareHeight()

  #落下済みのブロックを表示する
  for i in range(Board.BoardHeight):       
   for j in range(Board.BoardWidth):
    #i=0~21で、BoardHeight-i-1 = 21~0       
    shape = self.shapeAt(j, Board.BoardHeight - i - 1)
    #ブロック座標にセルがあるかないかを示すboardリストで 
    #shape=Tetrominoes.NoShape(0) であればセルがない事を示す 
    #もしセルが存在するならば、そのセルを表示する
    if shape != Tetrominoes.NoShape:
     self.drawSquare(dc,
         0 + j * self.squareWidth(),
         boardTop + i * self.squareHeight(), shape)

  #現在落下中のを描画します。
  #curPiece.shapeは初期値で0
  #curPieceは現在のブロックのオブジェクト
  #Shape()によってpieceShapeが0(Tetrominoes.NoShape)となる
  #しかしsetRandomShape()によって、pieceShapeがランダムに1~7のどれかになる
  if self.curPiece.shape() != Tetrominoes.NoShape:
   #ブロックは4つのセルでできているからrange(4)
   for i in range(4):
    #x(i) coords[index][0]を返す
    #curXの初期値は 6
    #xの衝突について考える
    x = self.curX + self.curPiece.x(i)
    y = self.curY - self.curPiece.y(i)
    #def drawSquare(self, dc, x, y, shape):
    self.drawSquare(dc, 0 + x * self.squareWidth(),
        boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
        self.curPiece.shape())

    #縦に22個のブロックのセルが表示できる
    #どんな形のブロックだろうと最初はy=21
    #どれかのセルがy=0となればそのセルは底辺についた事になる
    #coordsで表現されたブロックは上のコードにより上下逆に表示される
    #例えば coords = [[0, -1], [0, 0], [1, 0], [1, 1]] とすると
    #  ■    [1, 1]
    #  ■■  [0, 0]  [1, 0] 
    #  ■  [0, -1]

    #coords[x,-1]の時newPiece()の処理→上の処理後以下となる
    #つまりちょうどyの最低値(-1)で (Board.BoardHeight - y - 1)が
    #うまく 0 となるよう作られている

    #self.curY     20
    #self.curPiece.y(i) y  -1
    #y       21
    #Board.BoardHeight - y - 1   0

    #例えばcoords = [[0, 1], [0, 0], [1, 0], [1, 1]] とすると
    #  ■■  [0, 1]  [1, 1]
    #  ■■  [0, 0]  [1, 0] 

    #self.curY     21
    #self.curPiece.y(i) y  0
    #y       21
    #Board.BoardHeight - y - 1 0
    #つまりどのような形状であれ、最初のブロックの表示時ウィンドウ
    #の最上部にピッタリと隙間なく表示される(boardTopの隙間があるが)

 def OnKeyDown(self, event):                           #**********
  print "OnKeyDown"
  #startでisStarted=TrueになるしcurPiece.shape()=0という状態は
  #初期状態だし ifが成り立つのはありえないのでは?
  if not self.isStarted or self.curPiece.shape() == Tetrominoes.NoShape:
   event.Skip()
   return

  keycode = event.GetKeyCode()
  #ord アスキーコードを取得する
  #ゲームと止めたりすすめたり
  if keycode == ord('P') or keycode == ord('p'):
   self.pause()
   return

  #した2行のコードいらないと思うし、コメントアウトしても動作が変わらなか
  #った
  #ゲーム進行一時停止 
  if self.isPaused:
   return

  elif keycode == wx.WXK_SPACE:
   self.dropDown()
  else:
   event.Skip()

 
 def getkey(self, key):       
  return(bool(ctypes.windll.user32.GetAsyncKeyState(key)&0x8000))
 
 #私の環境ではカーソルイベントをOnKeyDownで拾えないので、OnTimerから
 #ここへジャンプさせてここで metaGetkeyでカーソルイベントを拾う
 def metaGetkey(self):      
  print "metaGetkey"
  global keycode
  keycode = 0
  if self.getkey(LEFT):
   keycode = wx.WXK_LEFT
   self.OnKeyDown_cursor()
   print "LEFT"
  if self.getkey(RIGHT):
   keycode = wx.WXK_RIGHT
   self.OnKeyDown_cursor()
   print "RIGHT"
  if self.getkey(DOWN):
   keycode = wx.WXK_UP
   self.OnKeyDown_cursor()
   print "DOWN"
  if self.getkey(UP):
   keycode = wx.WXK_DOWN
   self.OnKeyDown_cursor()
   print "UP"
  else:
   return

 
 def OnKeyDown_cursor(self):        
  print "OnKeyDown_cursor"
  global keycode
  print "keycode",keycode
  #OnKeyDown() メソッドで、押下されたキーを調べます。
  #左矢印キーを押すと、ブロックを左に 動かそうと します。
  #なぜ「動かそうとする」なのかというと、ブロックは移動でき
  #ないかもしれないからです。

  if keycode == wx.WXK_LEFT:
   print "elif keycode == wx.WXK_LEFT"
   self.tryMove(self.curPiece, self.curX - 1, self.curY)
  elif keycode == wx.WXK_RIGHT:
   self.tryMove(self.curPiece, self.curX + 1, self.curY)
  elif keycode == wx.WXK_DOWN:
   self.tryMove(self.curPiece.rotatedRight(), self.curX, self.curY)
  elif keycode == wx.WXK_UP:
   self.tryMove(self.curPiece.rotatedLeft(), self.curX, self.curY)
  elif keycode == wx.WXK_SPACE:
   self.dropDown()
  elif keycode == ord('D') or keycode == ord('d'):
   self.oneLineDown()
  else:
   pass

 #OnTimer() メソッドでは、以前のブロックが底に落下しきった後
 #に新しいブロックを生成したり、落下中のブロックを1行落としたりし
 #ます。
 #oneLineDown→tryMove(Refresh)→OnPaint→drawSquareで再描画されます
 #OnTimerの先にRefreshがあるため、タイマーのタイミングで再描画されます
 def OnTimer(self, event):       
  print
  print "Board OnTimer"
  self.metaGetkey()

  if event.GetId() == Board.ID_TIMER:                  #********
   if self.isWaitingAfterLine:
    self.isWaitingAfterLine = False
    self.newPiece()
   else:
    self.oneLineDown()
   #self.oneLineDown()
  else:
   event.Skip()


 #newpiece() メソッドはランダムに新しいブロックを生成します。
 #ブロックを初期位置に配置できなければ、ゲームオーバーです。
 def newPiece(self): 
  print "newPiece" 
  #現在のブロックのオブジェクトに次のブロックのオブジェクトを入れる
  self.curPiece = self.nextPiece

  #ブロック位置の初期化?
  #minYの動作は以下のとおり
  #self.coords [[0, -1], [0, 0], [1, 0], [1, 1]]とすると
  #minYは、coordsの中で一番ちいさなyを返す
  #例えばminYが0とすると、curYは21となる

  #実際の表示は
  # coordsとは上下が反転して表示される
  #  ■  curY=20  [0, -1]
  #  ■■  curY=21  [0, 0]  [1, 0]
  #    ■  curY=22  [1, 1]
  
  #self.curY = Board.BoardHeight - 1 、ブロックの上の部分が
  #切れて見えなくなる事がある
  #   _
  #   ■■ [0, 0][1, 0]
  #   ■
  #

  #self.curY = Board.BoardHeight とするとさらに切れてしまう
  #   __ [0, 0][1, 0]
  #  ■  [0, 1]

  #以上から、curYはブロックに対して最適な表示位置を示すパラメータと思われる

  #Shape()によってpieceShapeが0(Tetrominoes.NoShape)となる
  #しかしsetRandomShape()によって、pieceShapeがランダムに1~7のどれかになる
  self.nextPiece.setRandomShape()
  self.curX = Board.BoardWidth / 2 + 1   #6となる
  self.curY = Board.BoardHeight - 1 + self.curPiece.minY()

 def dropDown(self):                               #************
  print "dropDown"
  newY = self.curY
  while newY > 0:
   if not self.tryMove(self.curPiece, self.curX, newY - 1):
    break
   newY -= 1

  self.pieceDropped()

 #ブロック落下
 def oneLineDown(self):         
  print "oneLineDown"
  #tryMove=False ブロックが底に行っていたり他のブロックに接触していたら
  #self.curY - 1 によって落下する
  #tryMoveによってブロックが底、横壁、他のブロックに接触していたら
  #pieceDroppedへ行く
  if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
   #ブロック接触の処理
   #ブロックのどれかのセルが底辺に接触したら
   self.pieceDropped()

 #ブロックのセルが底辺または他のセルに接触したら
 def pieceDropped(self):        
  print "pieceDropped"
  for i in range(4):
   x = self.curX + self.curPiece.x(i)
   y = self.curY - self.curPiece.y(i)
   #curPiece.shape() 
   #ブロックの形状を示す番号を返す
   #ブロックのセルの座標x,yに対応するboardのリストの要素を
   #今のブロックの形状を示す数値に変更する
   self.setShapeAt(x, y, self.curPiece.shape())

  self.removeFullLines()                                #***************

  if not self.isWaitingAfterLine:
   self.newPiece()
   #pass
  #self.newPiece()


 #ブロックが底に当たると、 removeFullLines() メソッドを呼びま
 #す。 揃った行を見つけて、その行を削除します。 現在揃って
 #いる行を取り除き、その上の行全てを1行下げることでこれを
 #実現します。 削除される行の順番を逆にしていることに注意
 #してください。 そうでもしなければ、正しく動かないでしょ
 #うからね。 今回は単純な重力を使用しています。 つまり、
 #ブロックは空の隙間の上に浮かぶこともありうるということです。
 def removeFullLines(self):                  #********
  print "Board removeFullLines"
  numFullLines = 0 #ブロックが全部詰まっているy座標の個数を示す
  statusbar = self.GetParent().statusbar
  rowsToRemove = [] #ブロックが全部詰まっているy座標を記録する
  #BoardWidth  = 10  BoardHeight = 22

  #ブロック座標のどれかの横一列が全部詰まっていれば
  #そのy座標をrowsToRemoveに記録する
  for i in range(Board.BoardHeight):
   n = 0
   for j in range(Board.BoardWidth):
    #ブロック座標にどんな形状のブロックのセルがはいっているか
    #調べる
    if not self.shapeAt(j, i) == Tetrominoes.NoShape:
     #なにかしらのブロックのセルがあれば n+1
     n = n + 1
   #ある一列のブロック座標にブロックのセルが全部詰まっていれば
   #そのブロック座標のy座標を記録する
   if n == 10:
    rowsToRemove.append(i)

  #ブロックが横一列につまっている所全部を消す
  #リストの順番を逆にして並べ替え、つまり底辺のほうからになる
  rowsToRemove.reverse()
  for m in rowsToRemove:
   for k in range(m, Board.BoardHeight):
    for l in range(Board.BoardWidth):
     #m(k)のyブロック座標が一列セルで詰まっていれば
     #その列のセルのブロック形状を示す数値を
     #上のものに置き換える。つまり一列が消える?
     self.setShapeAt(l, k, self.shapeAt(l, k+1))

  #元コードこれより下余分に行頭にタブが入っていたようだ
  #それでステータスバーに表示される、横にブロックが揃った列の数が
  #増えて計上されていた。
  numFullLines += len(rowsToRemove)
  
  if numFullLines > 0:
   #横一列にブロックが詰まっている個数をnumLinesRemovedに代入
   #それをステータスバーのテキストにセットこれだけで表示できる
   self.numLinesRemoved += numFullLines
   statusbar.SetStatusText(str(self.numLinesRemoved))

   #ここでTrueとしておくと、OnTimer でoneLineDownでブロック落下の
   #操作に行かず newPiece 新しいブロック作成へ行く
   self.isWaitingAfterLine = True

   #現在のブロックのShapeを0としておかないと、今のブロックが表示されて
   #しまう。
   #pieceDroppedですでに現在のブロック情報はboardリストに転写済みな
   #のでこれでよい
   self.curPiece.setShape(Tetrominoes.NoShape)
   self.Refresh()

  
 #tryMove() メソッドで、ブロックを動かそうとします。 もしブロック
 #がゲーム盤の端だったり、他のブロックに隣接していたりすると
 #False を返します。 そうでなければ、現在落下中のブロックを新
 #しい位置に置き、 True を返します
 def tryMove(self, newPiece, newX, newY):    
  print "Board tryMove"
  for i in range(4):
   x = newX + newPiece.x(i)
   y = newY - newPiece.y(i)
   #  ■■■■■■ 横10  xは0~9
   #  ■■■■■■  縦22 yは上が21~下が0までとなる
   #  ■■■■■■
   #  ■■■■■■
   #
   #どれかのセルがy=0となればそのセルは底辺についた事になる
   #どんな形のブロックだろうと最初はy=21 BoardHeightは22
   #ブロックのセルが横壁か底に当たっていないか調べる
   if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
    return False
   #ブロックのセルがある所にすでに別のセルがありはしないか  
   if self.shapeAt(x, y) != Tetrominoes.NoShape:      
    return False

  #新しいnewX, newYをいれてぶつかっていないようであれば、curX curYに代入して
  #本当に移動させる

  #下一行のコード意味がない。なくてもいい
  #上間違い。単に移動だけならばいらないが、回転時仮のcurPieceを
  #作成していてそれが回転していても衝突しないようであれば
  #仮のcurPieceを、本当のcurPieceに代入している

  self.curPiece = newPiece
  self.curX = newX
  self.curY = newY
  #RefreshによってOnPaintが呼ばれる
  #OnPaintに描画へ行くコードあり 再描画される
  self.Refresh()
  return True

 def drawSquare(self, dc, x, y, shape):                       #********
  print
  print "doard drawSquare"
  #shape=0は全て黒になる
  colors = ['#000000', '#cc6666', '#66cc66', '#6666cc',
      '#cccc66', '#cc66cc', '#66cccc', '#daaa00']

  light  = ['#00###0000', '#f89fab', '#79fc79', '#7979fc',
      '#fcfc79', '#fc79fc', '#79fcfc', '#fcc600']

  dark   = ['#000000', '#803c3b', '#3b803b', '#3b3b80',
      '#80803b', '#803b80', '#3b8080', '#806200']

  #ブロックのセルに立体感を出すためセルの辺を以下で描画
  #E:\MyBackups\goolgedrive\myprg_main\python_my_prg\wxpython\dc\dc_mouse_drawo.py
  #上のファイルでこのセル表示の実験をする
  #*************************************************
  pen = wx.Pen(light[shape])
  pen.SetCap(wx.CAP_PROJECTING)
  dc.SetPen(pen)

  #squareHeight squareWidth ブロックのセルの高さ幅をしめす
  #セルの上と左の辺を上のペンでラインを引く
  dc.DrawLine(x, y + self.squareHeight() - 1, x, y)
  dc.DrawLine(x, y, x + self.squareWidth() - 1, y)


  darkPen = wx.Pen(dark[shape])
  #理想のユーザ・インターフェイスを求めて
  #https://ideal-user-interface.hatenablog.com/entry/20080529/1212061067
  #端点のスタイルを指定(直線で切断)
  #コメントアウトしてもあんまり違いがわからない
  darkPen.SetCap(wx.CAP_PROJECTING)
  dc.SetPen(darkPen)

  #セルの下と右の辺を上のペンで描く
  dc.DrawLine(x + 1, y + self.squareHeight() - 1,
     x + self.squareWidth() - 1, y + self.squareHeight() - 1)
  dc.DrawLine(x + self.squareWidth() - 1, y + self.squareHeight() -1,
     x + self.squareWidth() - 1, y + 1)
  #*************************************************
  
  dc.SetPen(wx.TRANSPARENT_PEN)
  dc.SetBrush(wx.Brush(colors[shape]))
  dc.DrawRectangle(x + 1, y + 1, self.squareWidth() - 2,
       self.squareHeight() - 2)

  #http://xoomer.virgilio.it/infinity77/wxPython/Widgets/wx.Pen.html
  #定義済みのペン
  #wx.RED_PEN
  #wx.CYAN_PEN
  #wx.GREEN_PEN
  #wx.BLACK_PEN
  #wx.WHITE_PEN
  #wx.TRANSPARENT_PEN
  #wx.BLACK_DASHED_PEN
  #wx.GREY_PEN
  #wx.MEDIUM_GREY_PEN
  #wx.LIGHT_GREY_PEN
  #dc.SetPen(wx.TRANSPARENT_PEN)
  #dc.SetPen(wx.RED_PEN)
  #TRANSPARENT_PENを実際使ったところなんの色でも描画されいなかった。
  #コメントアウトしてもどうもなかったが、なにか必要なのだろう



#shape ブロックの形状を決める
class Tetrominoes(object):
 NoShape  = 0
 ZShape   = 1
 SShape   = 2
 LineShape   = 3
 TShape   = 4
 SquareShape = 5
 LShape   = 6
 MirroredLShape = 7

#Shape クラスはテトリブロックの情報を保持しています。
class Shape(object):
 coordTable = (
  ((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 __init__(self):
  #ブロックの作成中に、 空の座標リストを作成します。 この
  #リストは、テトリブロックの座標を保持します。 例えば、(0,
  #  -1), (0, 0), (1, 0), (1, 1) のタプルは回転し
  #たS-テトリブロックを表します。 以下の図表がブロックを図示し
  #ています。

  #[[0, 0], [0, 0], [0, 0], [0, 0]]を作成
  self.coords = [[0, 0] for i in range(4)]
  #0をセット
  self.pieceShape = Tetrominoes.NoShape
  self.setShape(Tetrominoes.NoShape)

 def shape(self):
  print
  print "Shape def shape"
  #ブロックの形状をしめす番号を返す coordTableのインデックス
  return self.pieceShape

 #pieceShapeにインデックスshapeをセットする
 #coordsにインデックスshapeのブロック座標をセットする
 def setShape(self, shape):
  print
  print "setShape"
  #shape初期値は0
  #shapeにより coordTableの8つのうち一つを選ぶ
  #それをcoordsにセットする
  table = Shape.coordTable[shape]
  for i in range(4):
   for j in range(2):
    self.coords[i][j] = table[i][j]

  #shapeをセット
  self.pieceShape = shape
  print "self.pieceShape",self.pieceShape

 #ランダムなブロックを一個作成
 def setRandomShape(self):
  print "setRandomShape "
  #1~7までの乱数を発生
  #self.setShape(random.randint(1, 7))
  self.setShape(5)


 def x(self, index):
  return self.coords[index][0]

 def y(self, index):
  return self.coords[index][1]

 def setX(self, index, x):
  self.coords[index][0] = x

 def setY(self, index, y):
  self.coords[index][1] = y

 #coordsの中で一番ちいさなxを返す
 def minX(self):
  m = self.coord[0][0]
  for i in range(4):
   m = min(m, self.coords[i][0])

  return m

 #coordsの中で一番大きいxを返す
 def maxX(self):
  m = self.coord[0][0]
  for i in range(4):
   m = max(m, self.coords[i][0])

  return m

 #self.coords [[0, -1], [0, 0], [1, 0], [1, 1]]とすると
 #coordsの中で一番ちいさなyを返す
 def minY(self):
  m = self.coords[0][1]
  for i in range(4):
   m = min(m, self.coords[i][1])
  return m

 #coordsの中で一番大きいyを返す
 def maxY(self):
  m = self.coords[0][1]
  for i in range(4):
   m = max(m, self.coords[i][1])

  return m

 #時計まわりに回転
 def rotatedLeft(self):
  # SquareShape:5
  # ブロックの形状が5ならなにもしない
  #上の形状は座標の第一象限に四角のブロック形状なので
  #回転させると、四角の左下の原点を中心にして回転してしまう
  #ので振動したように見えてこれを回避しているのだろう
  if self.pieceShape == Tetrominoes.SquareShape:
   return self

  #回転させた仮のブロックのオブジェクトを作成
  #tryMoveによって回転後どこにもあたらないを判断されたら
  #tryMoveの self.curPiece = newPiece によって今のブロック形状
  #に代入される。

  #ブロックの形状を初期化
  result = Shape()
  #今のブロック形状を仮のブロック形状にいれる
  result.pieceShape = self.pieceShape
  #ブロック形状座標のi番目の要素のx成分と、y成分を取り替え
  #x成分に-をつける
  #上で座標に印をつけて確認したところ、確かにブロック座標が
  #時計まわりに回転する。
  for i in range(4):
   result.setX(i, self.y(i))
   result.setY(i, -self.x(i))

  return result

 #反時計回りに回転
 def rotatedRight(self):
  if self.pieceShape == Tetrominoes.SquareShape:
   return self

  result = Shape()
  result.pieceShape = self.pieceShape
  for i in range(4):
   result.setX(i, -self.y(i))
   result.setY(i, self.x(i))

  return result



#thread = threading.Thread(target=metaGetkey)
#thread.start()

#app = wx.App()
app = MyApp()
tetris = Tetris(None, -1, 'tetris.py')
app.MainLoop()


 

0 件のコメント:

コメントを投稿

About

参加ユーザー

連絡フォーム

名前

メール *

メッセージ *

ブログ アーカイブ

ページ

Featured Posts