2019年3月28日木曜日

WE408A三結の低電圧特性

WE403A(6AK5)のヒーターが20Vになったバージョンである,WE408Aの三結について,低電圧特性を測ってみました.


WE 408A MT7pin




五極管 ヒーター 20V 50mA



三極管接続 Eg2=Ep







Eg

Ip(mA) 0 -0.2 -0.4 -0.6 -0.8 -1
Ep 0 0.013 0.006 0.003 0.002 0.002 0.001
1 0.017 0.008 0.004 0.002 0.002 0.001
2 0.024 0.011 0.005 0.002 0.002 0.001
3 0.034 0.016 0.007 0.003 0.002 0.001
4 0.046 0.023 0.011 0.004 0.003 0.001
5 0.061 0.031 0.015 0.006 0.003 0.001
6 0.08 0.042 0.02 0.009 0.004 0.002
7 0.102 0.056 0.028 0.013 0.006 0.002
8 0.129 0.074 0.037 0.018 0.008 0.003
9 0.161 0.093 0.049 0.024 0.011 0.004
10 0.199 0.117 0.063 0.032 0.014 0.006
11 0.238 0.143 0.08 0.042 0.02 0.008
12 0.277 0.173 0.099 0.054 0.026 0.011
13 0.29 0.207 0.122 0.068 0.034 0.015
14 0.3 0.243 0.147 0.085 0.043 0.02
15 0.31 0.282 0.176 0.103 0.054 0.027
16 0.32 0.29 0.208 0.125 0.066 0.033
17 0.33 0.29 0.244 0.149 0.081 0.042
18 0.38 0.3 0.283 0.172 0.098 0.052

まず,次のグラフがEp-Ip特性です.それぞれの折れ線のラベルがEgの値(V)です.6AK5(403A)は内部抵抗が大きいので,この程度の低電圧だと1mAも流れていませんが,そこそこ綺麗な三極管特性がこの程度の電圧でも見られます.Eg=0Vと0.2Vのとき,0.3mAぐらいでサチってしまっています.これが何かは,もう少し広い範囲で見てみないと分かりません.

次のグラフがEg-Ip特性です.それぞれの折れ線のラベルがEpの値(V)です.

これらの特性を見ると,普段プレート電圧が100V単位のスケールで見ている傾向が,10V程度でも見えます.負荷抵抗を大きくすれば,12Vや30V程度のB電圧で十分使えそうです.


2019年3月24日日曜日

Pythonでゲーム作成(Hextrisゲーム)

「Pythonでテトリス」を書き換えてHextrisを作ってみました.

画面はこんな感じです.


昔,X68000で遊んでいたゲームです.最近は同じ名前の別のゲームがあるようですが...

以下がソースコードです.


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Python 2.7.5 on OS X 10.9.2
# by Mitsuharu Arimura 2015/3/14

import Tkinter as Tk
import datetime as dt
import time, random, math
import sys, os, csv

# ゲームフィールドのサイズ
FIELD_WIDTH = 17
FIELD_HEIGHT = 24

# ブロックのサイズ
PIECE_WIDTH = 4
PIECE_HEIGHT = 4

# 1個の六角形セルの1辺の長さ
CELL_SIZE = 15

SQRT3 = math.sqrt(3.0)

# 六角形を作る関数
def CreateHexagon( canvas, x, y, offset ):
    # (x0, y0): 六角形の中心
    x0 = CELL_SIZE * (x * 1.5 + 1) + offset
    y0 = CELL_SIZE * (y + 0.5 + x * 0.5) * SQRT3 + offset
    #print "(%d, %d): (%.1f, %.1f)" % (x, y, x0, y0)
    # vtx_x, vty_y: 六角形の頂点のx座標と座標の配列(6個)
    vtx_x = [ x0 + CELL_SIZE * math.cos(i*math.pi/3.0)
              for i in range(0, 6) ]
    vtx_y = [ y0 + CELL_SIZE * math.sin(i*math.pi/3.0)
              for i in range(0, 6) ]
    #print "x: ", vtx_x
    #print "y: ", vtx_y
    r = canvas.create_polygon( vtx_x[0], vtx_y[0],
                             vtx_x[1], vtx_y[1],
                             vtx_x[2], vtx_y[2],
                             vtx_x[3], vtx_y[3],
                             vtx_x[4], vtx_y[4],
                             vtx_x[5], vtx_y[5],
                             fill='white',
                             outline='gray',
                             width=1)
    return r

# ゲーム本体の画面 View
class Board( Tk.Canvas ):
    # フィールドのピクセル数
    BOARD_WIDTH = (FIELD_WIDTH + 0.5) * CELL_SIZE * 1.5
    BOARD_HEIGHT = (FIELD_HEIGHT + 0.5) * CELL_SIZE * SQRT3 + 3
    margin = 2

    def __init__( self, master ):
        self.width = self.BOARD_WIDTH
        self.height = self.BOARD_HEIGHT
        Tk.Canvas.__init__( self, master, relief=Tk.RAISED, bd=2, bg='gray',
                            width=self.BOARD_WIDTH, height=self.BOARD_HEIGHT )
        #print "Canvas width %d x %d" % ( self.BOARD_WIDTH, self.BOARD_HEIGHT )

        # Canvas上の rectangle の2次元配列
        # 周囲に1マスずつ余計に取って,グレーに塗り潰しておくのが簡単
        self.Field = [[0 for y in range(- int(x*0.5), FIELD_HEIGHT-int(x*0.5))]
                      for x in range(FIELD_WIDTH)]
        offset = self.margin * 2 + 3
        for x in range( FIELD_WIDTH ):
            for y in range( -int(x*0.5), FIELD_HEIGHT-int(x*0.5) ):
                self.Field[x][y] = CreateHexagon( self, x, y, offset )

        # PAUSEの文字
        TEXT_font = ('Helvetica','60','bold')
        TEXT_color = 'red'
        self.pause_text = self.create_text( self.BOARD_WIDTH/2, 100,
                                            text='PAUSE',
                                            font=TEXT_font,
                                            fill=TEXT_color,
                                            justify=Tk.CENTER)
        self.hidePauseText()

    def SetColor( self, x, y, value ):
        #print "SetColor:"
        if x < 0: return
        if x >= FIELD_WIDTH: return
        if y < -int(x * 0.5): return
        if y >= FIELD_HEIGHT - int(x * 0.5): return
        #print "(%d, %d)=>%d" % (x, y, value)
        if value == 1:
            self.itemconfigure( self.Field[x][y], fill='black')
        elif value == 2:
            self.itemconfigure( self.Field[x][y], fill='gray')
        elif value == 3:
            self.itemconfigure( self.Field[x][y], fill='slate gray')
        else:
            self.itemconfigure( self.Field[x][y], fill='white')

    # PAUSE中にPAUSEの文字を表示する
    def showPauseText( self ):
        self.itemconfigure( self.pause_text, state=Tk.NORMAL )

    # PAUSEが終わるときにPAUSEの文字を非表示にする
    def hidePauseText( self ):
        self.itemconfigure( self.pause_text, state=Tk.HIDDEN )

# 次のピースを表示する画面 View
class NextPieceBoard( Tk.Canvas ):
    # 次のピースを表示する場所のサイズ
    NEXT_BOARD_WIDTH = (PIECE_WIDTH + 0.5) * CELL_SIZE * 1.5
    NEXT_BOARD_HEIGHT = (PIECE_HEIGHT + 1.5) * CELL_SIZE * SQRT3 + 3
    margin = 2
    def __init__( self, master ):
        Tk.Canvas.__init__( self, master, relief=Tk.RAISED, bd=self.margin,
                            bg='white',
                            width=self.NEXT_BOARD_WIDTH,
                            height=self.NEXT_BOARD_HEIGHT )
        self.RectArray = [[0 for y in range(PIECE_HEIGHT)]
                          for x in range(PIECE_WIDTH)]
        #print self.RectArray
        offset = self.margin * 2 + 3
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                self.RectArray[x][y] = CreateHexagon( self, x, y, offset )

    def DisplayPiecePattern( self, pattern ):
        p = pattern
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                #print "p[%d][%d] = %d" % (x, y, p[x][y])
                if p[x][y] == 1:
                    # print "p[%d][%d] = red" % (x, y)
                    self.itemconfigure( self.RectArray[x][y], fill='black' )
                else:
                    # print "p[%d][%d] = blue" % (x, y)
                    self.itemconfigure( self.RectArray[x][y], fill='white' )

# ゲーム全体の画面および Controller
class Frame( Tk.Frame ):
    def __init__( self, master=None ):
        Tk.Frame.__init__( self, master, width=600, height=800 )
        self.master.title( 'Hextris' )

        #########################################
        # board frame
        #BOARD_WIDTH = (FIELD_WIDTH + 0.5) * CELL_SIZE * 1.5
        #BOARD_HEIGHT = (FIELD_HEIGHT + 0.5) * CELL_SIZE * SQRT3 + 3
        self.bframe = Tk.Frame( self, bd=1, relief=Tk.RIDGE )
        self.bframe.pack( side=Tk.LEFT, padx=10, pady=10 )
        self.board = Board( self.bframe )
        self.board.pack( side=Tk.LEFT, padx=0, pady=0 )

        #########################################
        # display frame
        frame = Tk.Frame( self, bd=1, relief=Tk.RIDGE )
        # exit button
        self.btExit = Tk.Button( frame, text='Exit', command=self.exitGame )
        self.btExit.pack( anchor=Tk.E, padx=10, pady=10 )
        # replay (not start) button
        #self.btReplay = Tk.Button( frame, text='Replay',
        #                           command=self.replayGame )
        #self.btReplay.pack( anchor=Tk.E, padx=10, pady=0 )
        # start button
        self.btStart = Tk.Button( frame, text='Start', command=self.startGame )
        self.btStart.pack( anchor=Tk.E, padx=10, pady=0 )
        # next piece display
        self.next_piece = NextPieceBoard( frame )
        self.next_piece.pack( anchor = Tk.E, padx=10, pady=10 )
        TEXT_font = ('Helvetica','12','italic')
        NUM_font = ('Helvetica','24','bold')
        # time text
        self.time_text = Tk.Label( frame, text='PLAY TIME', font=TEXT_font )
        self.time_text.pack( anchor = Tk.E, padx=10, pady=0 )
        # timer
        self.time_box = Tk.Label( frame, text='00\'00\"', font=NUM_font,
                                  bd=1, relief=Tk.RIDGE )
        self.time_box.pack( anchor = Tk.E, padx=10, pady=0 )
        frame.pack( side=Tk.RIGHT, padx=10, pady=10 )

        # key bind
        self.bind( "", self.keyPressed )
        self.bind( "", self.keyLeft )
        self.bind( "", self.keyRight )
        self.bind( "", self.keyUp )
        self.bind( "", self.keyDown )
        self.focus_set()

        self.hextris = Hextris( self )

        self.processing_down = False
        self.timerEventCount = 0
        self.timerCount = 0

        #self.DisableReplayButton()
        self.hextris.PrepareStartGame()

    # exit button
    def exitGame( self ):
        self.hextris.playing = False
        sys.exit()

    # start button
    def startGame( self ):
        self.hextris.playing = True
        # ボタンは押せなくする
        self.DisableStartButton()
        #self.DisableReplayButton()
        # カウンタをリセットしてタイマーをスタートする
        self.timerEventCount = 0
        self.timerCount = 0
        # これ以降,timerEventが0.5秒に一回呼び出される
        self.after( 500, self.timerEvent )

    # prepare replay
    def replayGame( self ):
        self.EnableStartButton()
        self.hextris.PrepareStartGame()
        self.timerEventCount = 0
        self.timerCount = 0
        self.DisplayTime( 0, 0 )

    # 0.5秒ごとに呼び出される
    def timerEvent( self ):
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        self.timerEventCount = self.timerEventCount + 1
        # 0.5秒に1回呼び出されるので2回→1秒に1回の点数アップ
        if self.timerEventCount % 2 == 0:
            self.timerCount = self.timerCount + 1
            self.DisplayTime( self.timerCount / 60, self.timerCount % 60 )
            # print "count %d" % self.timerEventCount
            # print "timer %d" % self.timerCount
        # ブロックを1段落とす
        self.keyDown( None )
        # 0.5秒後に自分を呼び出す
        self.after( 500, self.timerEvent )

    def endGame( self ):
        self.focus_set()
        self.replayGame()

    def DisableExitButton( self ):
        self.btExit.configure( state=Tk.DISABLED )

    def EnableExitButton( self ):
        self.btExit.configure( state=Tk.NORMAL )

    def DisableReplayButton( self ):
        #self.btReplay.configure( state=Tk.DISABLED )
        pass

    def EnableReplayButton( self ):
        #self.btReplay.configure( state=Tk.NORMAL )
        pass

    def DisableStartButton( self ):
        self.btStart.configure( state=Tk.DISABLED )

    def EnableStartButton( self ):
        self.btStart.configure( state=Tk.NORMAL )

    def DisplayTime( self, min=0, sec=0 ):
        self.time_box.configure( text="%02d\'%02d\"" % (min, sec) )

    # 以下はキー入力処理
    def keyPressed( self, event ):
        c = event.char
        # space: pause
        if self.hextris.playing == False:
            return
        if c == ' ':
            if self.hextris.pausing == False:
                print "PAUSE!!!"
                self.hextris.pausing = True
                # PAUSEを表示
                self.board.showPauseText()
            else:
                print "PAUSE END!!!"
                self.hextris.pausing = False
                # PAUSEを隠す
                self.board.hidePauseText()
                # 再度タイマーを開始
                self.after( 500, self.timerEvent )

    def keyLeft( self, event ):
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        # print "press Left"
        if self.hextris.CanMovePiece( move='left' ) == 0:
            self.hextris.MovePiece( move='left' )
            self.hextris.RedrawAllField()

    def keyRight( self, event ):
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        # print "press Right"
        if self.hextris.CanMovePiece( move='right' ) == 0:
            self.hextris.MovePiece( move='right' )
            self.hextris.RedrawAllField()

    # キーで呼ばれるのとタイマーで呼ばれるのが同時に重なる場合が
    # あるので再入防止している
    def keyDown( self, event ):
        #print "keyDown"
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        # print "press Down"
        if self.processing_down == True:
            return
        self.processing_down = True
        res = self.hextris.CanMovePiece( move='down' )
        if res == 0:
            self.hextris.MovePiece( move='down' )
            self.hextris.RedrawAllField()
        elif res == 2:
            self.hextris.FixPieceAndGetNextPiece()
        self.processing_down = False

    # 回転
    def keyUp( self, event ):
        if self.hextris.playing == False:
            return
        if self.hextris.pausing == True:
            return
        # print "press Up"
        if self.hextris.CanTurnPiece() == True:
            self.hextris.TurnPiece()
            self.hextris.RedrawAllField()

# Hextrisゲームの Data
class Hextris:
    # ゲームフィールド
    field = [[0 for y in range(FIELD_HEIGHT)]
             for x in range(FIELD_WIDTH)]
    # 現在移動中のブロック
    piece = [[0 for y in range(PIECE_HEIGHT)]
             for x in range(PIECE_WIDTH)]
    # ブロックの左上端の座標(フィールドは左上が(x,y) = (0,0)でyが下向き)
    location = [0, 0]

    # ブロック
    # 種類の数を揃えておくこと!!!
    # listにaddしていくと,ここで数を書く必要は無い
    ID_NUM = 10
    id = 0 # 0...ID_NUM-1の8種類
    id_next = 0 # 次のピース
    ORIENT_NUM = 6
    orientation = 0 # 0...ORIENT_NUM-1の6種類

    # 次のブロック
    nextPiece = [[[0 for y in range(PIECE_HEIGHT)]
                  for x in range(PIECE_WIDTH)]
                 for o in range(ORIENT_NUM)]

    # ブロックの全てのパターン index, orientation, x, y毎に1, 0
    piecePattern = [[[[0 for y in range(PIECE_HEIGHT)]
                      for x in range(PIECE_WIDTH)]
                     for o in range(ORIENT_NUM)]
                    for i in range(ID_NUM)]
    piecePattern[0] = [
        [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]], # 方向1
        [[0,0,0,1],[0,0,1,0],[0,1,0,0],[1,0,0,0]], # 方向2
        [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], # 方向3
        [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]], # 方向4
        [[0,0,0,1],[0,0,1,0],[0,1,0,0],[1,0,0,0]]] # 方向5
    piecePattern[1] = [
        [[0,0,0,0],[1,1,0,0],[0,1,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,0,1,0],[0,0,1,0],[0,1,0,0],[0,1,0,0]], # 方向1
        [[0,0,0,1],[0,1,1,0],[1,0,0,0],[0,0,0,0]], # 方向2
        [[0,0,0,0],[1,1,0,0],[0,1,1,0],[0,0,0,0]], # 方向3
        [[0,0,1,0],[0,0,1,0],[0,1,0,0],[0,1,0,0]], # 方向4
        [[0,0,0,1],[0,1,1,0],[1,0,0,0],[0,0,0,0]]] # 方向5
    piecePattern[2] = [
        [[0,0,0,0],[0,0,1,1],[1,1,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]], # 方向1
        [[0,0,1,0],[0,1,0,0],[0,1,0,0],[1,0,0,0]], # 方向2
        [[0,0,0,0],[0,0,1,1],[1,1,0,0],[0,0,0,0]], # 方向3
        [[0,1,0,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]], # 方向4
        [[0,0,1,0],[0,1,0,0],[0,1,0,0],[1,0,0,0]]] # 方向5
    piecePattern[3] = [
        [[0,1,0,0],[1,1,0,0],[1,0,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,1,0],[1,1,0,0],[0,0,0,0],[0,0,0,0]], # 方向1
        [[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]], # 方向2
        [[0,1,0,0],[1,1,0,0],[1,0,0,0],[0,0,0,0]], # 方向3
        [[0,1,1,0],[1,1,0,0],[0,0,0,0],[0,0,0,0]], # 方向4
        [[0,1,1,0],[0,1,1,0],[0,0,0,0],[0,0,0,0]]] # 方向5
    piecePattern[4] = [
        [[0,0,0,0],[0,0,0,1],[1,1,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,0,0],[0,1,1,0],[0,0,0,0]], # 方向1
        [[0,0,0,1],[0,0,1,0],[0,1,0,0],[0,1,0,0]], # 方向2
        [[0,0,0,0],[0,1,1,1],[1,0,0,0],[0,0,0,0]], # 方向3
        [[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]], # 方向4
        [[0,0,1,0],[0,0,1,0],[0,1,0,0],[1,0,0,0]]] # 方向5
    piecePattern[5] = [
        [[0,0,0,0],[1,1,1,0],[0,0,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,0,0],[0,1,0,0],[1,0,0,0]], # 方向1
        [[0,0,0,1],[0,0,1,0],[1,1,0,0],[0,0,0,0]], # 方向2
        [[0,0,0,0],[1,0,0,0],[1,1,1,0],[0,0,0,0]], # 方向3
        [[0,1,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0]], # 方向4
        [[0,0,1,1],[0,1,0,0],[1,0,0,0],[0,0,0,0]]] # 方向5
    piecePattern[6] = [
        [[0,0,0,0],[0,0,1,0],[1,1,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,1,0],[0,1,0,0],[0,0,0,0]], # 方向1
        [[0,0,1,0],[0,1,0,0],[1,1,0,0],[0,0,0,0]], # 方向2
        [[0,0,0,0],[0,1,1,1],[0,1,0,0],[0,0,0,0]], # 方向3
        [[0,0,1,0],[0,1,1,0],[0,0,1,0],[0,0,0,0]], # 方向4
        [[0,0,0,0],[0,0,1,1],[0,0,1,0],[0,1,0,0]]] # 方向5
    piecePattern[7] = [
        [[0,0,0,0],[0,1,1,1],[0,0,1,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,0,1,0],[0,0,1,0],[0,1,1,0],[0,0,0,0]], # 方向1
        [[0,0,0,0],[0,0,1,0],[1,1,0,0],[1,0,0,0]], # 方向2
        [[0,0,0,0],[0,1,0,0],[1,1,1,0],[0,0,0,0]], # 方向3
        [[0,1,1,0],[0,1,0,0],[0,1,0,0],[0,0,0,0]], # 方向4
        [[0,0,1,0],[0,1,1,0],[1,0,0,0],[0,0,0,0]]] # 方向5
    piecePattern[8] = [
        [[0,1,1,0],[1,0,0,0],[1,0,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,1,0],[1,0,1,0],[0,0,0,0],[0,0,0,0]], # 方向1
        [[0,1,1,0],[0,0,1,0],[0,1,0,0],[0,0,0,0]], # 方向2
        [[0,0,1,0],[0,0,1,0],[1,1,0,0],[0,0,0,0]], # 方向3
        [[0,0,0,0],[1,0,1,0],[1,1,0,0],[0,0,0,0]], # 方向4
        [[0,1,0,0],[1,0,0,0],[1,1,0,0],[0,0,0,0]]] # 方向5
    piecePattern[9] = [
        [[0,0,1,0],[1,1,0,0],[0,1,0,0],[0,0,0,0]], # 方向0の[x][y]
        [[0,1,0,0],[0,1,1,0],[1,0,0,0],[0,0,0,0]], # 方向1
        [[0,0,1,0],[1,1,0,0],[0,1,0,0],[0,0,0,0]], # 方向2
        [[0,1,0,0],[0,1,1,0],[1,0,0,0],[0,0,0,0]], # 方向3
        [[0,0,1,0],[1,1,0,0],[0,1,0,0],[0,0,0,0]], # 方向4
        [[0,1,0,0],[0,1,1,0],[1,0,0,0],[0,0,0,0]]] # 方向5

    def __init__( self, parent ):
        self.main_frame = parent
        self.main_board = parent.board
        self.next_piece = parent.next_piece

    # ゲームの開始準備
    def PrepareStartGame( self ):
        self.playing = False
        self.pausing = False
        self.game_end = False

        # 盤面の初期化
        # 全部を白で塗る
        for y in range( FIELD_HEIGHT ):
            for x in range( FIELD_WIDTH ):
                self.field[x][y] = 0
        # 周囲1列を3で塗る
        for y in range( FIELD_HEIGHT ):
            self.field[0][y] = 3
        for x in range( FIELD_WIDTH ):
            self.field[x][ -int(x*0.5)+FIELD_HEIGHT - 1 ] = 3
        for y in range( FIELD_HEIGHT ):
            self.field[ FIELD_WIDTH-1 ][-int((FIELD_WIDTH-1)*0.5)+y] = 3

        # 1個目のピースを準備する
        self.GetNextPiece( True )
        self.RedrawAllField()

    # 1行だけ消す処理
    # offset: 0 or 1
    def DeleteOneLine( self, line, offset ):
        # 上から1行落とす
        # 下から上に向かって1行ずつ,上の行を下の行にコピーする
        for y in range(line, 1, -1):
            for x in range(1,FIELD_WIDTH-1):
                # offsetが0か1かでジグザグのパターンが変わる
                y0 = y-int((x+offset)*0.5)
                self.field[x][y0] = self.field[x][y0-1]
        self.RedrawAllField()

    # 埋まっている行を全て消す処理
    def DeleteFilledLines( self ):
        #print "Start Delete Line!!!"
        # yとoffsetで両方のパターンのジグザグのカウントをする
        y = FIELD_HEIGHT - 2 # 一番下の行を除く.
        offset = 0
        deleted_line = 0 # 削除できた行の数
        all_clear = 0 # 残りが0になった場合
        # 一番上の行まで確認
        count_sum = 0
        while y >= 0:
            # 1行に埋まってるセルの個数を数える
            #print "count line %d, offset %d:" % (y, offset),
            # 1行のカウント
            count = 0
            for x in range(1, FIELD_WIDTH-1): # 両端を除く
                #print "%d" % self.field[x][y-int((x+offset)*0.5)],
                # offsetが0か1かでジグザグのパターンが変わる
                if self.field[x][y-int((x+offset)*0.5)] == 2:
                    count = count + 1
            #print "count %d" % count
            # 全部が埋まっていたら行の削除処理
            if count == FIELD_WIDTH - 2: # 両端を除いた数
                # 削除できた場合にはyは増えない offsetも増えないのが良い
                #print "clear line %d" % y
                self.DeleteOneLine(y, offset)
                deleted_line = deleted_line + 1
            else:
                count_sum = count_sum + count
                # 全部が埋まっていなかったら次の行へ行く
                if offset == 0:
                    offset = 1
                else: # offset == 1
                    offset = 0
                    y = y - 1
        print "end Delete Line!!! (%d lines)" % deleted_line
        print "%d cells left (including double counting)" % count_sum

        # count_sumが0だったらボーナス
        if count_sum == 0:
            all_clear == 1
            print '########################################'
            print '######## ALL CELLS CLEARED #############'
            print '########################################'

    # 現在のピースが落ち切ったので,ここに固定する
    # 行が埋まったら消す
    # 次のピースを開始する
    def FixPieceAndGetNextPiece( self ):
        # 現在のピースを現在の位置に固定する
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                if self.piece[x][y] == 1:
                    self.field[self.location[0]+x][self.location[1]+y] = 2
        # 埋まっている行を消す
        self.DeleteFilledLines()
        # 次のピース
        self.GetNextPiece( False )
        self.RedrawAllField()

    # next pieceの領域に次のピースを作成する
    def CreatePiece( self ):
        self.id_next = random.randint(0, len(self.piecePattern)-1)
        #self.id_next = 9
        #print "piece pattern [%d]" % self.id_next,
        self.orientation = 0
        #print "piece orientation [%d]" % self.orientation
        # パターンi
        self.nextPiece = self.piecePattern[self.id_next]
        #print "-->", self.nextPiece
        #print "orientation 0 -->", self.nextPiece[0]
        # 向き0を表示する
        self.next_piece.DisplayPiecePattern( self.nextPiece[0] )

    # next pieceの領域からピースを持ってきて一番上に置く
    # 既にここまでピースが積んであったらゲームオーバー判定
    def GetNextPiece( self, is_first ):
        if is_first:
            self.CreatePiece()
        #print self.nextPiece
        self.location[0] = 5
        self.location[1] = -2
        # 次のピースを持ってくる(向きは0方向)
        self.id = self.id_next
        self.piece = self.piecePattern[self.id][0]
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                # フィールドに置く
                self.main_board.SetColor( self.location[0]+x,
                                          self.location[1]+y,
                                          self.piece[x][y])
        # ゲーム終了判定
        for y in range( PIECE_HEIGHT ):
            # 内側のxのloopから抜けて来ていたら,ここでも抜ける
            if self.game_end == True:
                break
            for x in range( PIECE_WIDTH ):
                # フィールドに置いた瞬間,既にその場所にブロックがあったら
                if ( self.piece[x][y] == 1 and
                     self.field[self.location[0]+x][self.location[1]+y] == 2):
                    # 終わり
                    self.game_end = True
                    # 次回でタイマーでのloopを止める
                    self.playing = False
                    # 1個重なっていれば十分なので,ここで抜ける
                    break
        if self.game_end == False:
            # 終わっていなかったら続ける
            self.CreatePiece()
        else:
            # ゲームオーバー処理の開始
            print "GAME END !!!"
            self.main_frame.endGame()

    def MoveOrientation( self, move ):
        xmove = ymove = 0
        if move == 'down':
            ymove = 1
        elif move == 'left':
            xmove = -1
            if self.location[0] % 2 == 1:
                ymove = 1
        elif move == 'right':
            xmove = 1
            if self.location[0] % 2 == 0:
                ymove = -1
        return ( xmove, ymove )

    def MovePiece( self, move ):
        ( xmove, ymove ) = self.MoveOrientation( move )
        self.location[0] = self.location[0] + xmove
        self.location[1] = self.location[1] + ymove

    def TurnPiece( self ):
        self.orientation = (self.orientation + 1) % self.ORIENT_NUM
        self.piece = self.piecePattern[self.id][self.orientation]

    # 移動した先を計算して,fieldとかぶらないか,はみ出ないかを見る
    # return 1: 左右の壁に当たって移動できない
    # return 2: 積んであるフィールド上のセルと重なるか,
    #           下に当たっているので,これで固定する
    def CanMovePiece( self, move='down' ):
        ( xmove, ymove ) = self.MoveOrientation( move )
        ( x0, y0 ) = ( self.location[0], self.location[1] )
        # フィールド上のセルと重なっている場合
        flag = 0
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                if ( self.piece[x][y] == 1 and
                     self.field[x0+x+xmove][y0+y+ymove] >= 1 ):
                    flag = 1
                    #print "move NG (4) (%d,%d)->(%d,%d)" % (
                    #    self.location[0]+x, self.location[1]+y,
                    #    self.location[0]+x+xmove, self.location[1]+y+ymove )
                    break
        if flag == 1:
            if ymove == 1:
                return 2 # 下に動けない場合はこれで固定
            else:
                return 1 # 左右ぶつかっている場合は,動けないだけ
        # 以上のチェックに引っかからない場合は動ける
        return 0

    # 回転した先を計算して,fieldとかぶらないか,はみ出ないかを見る
    # True: 回転できる
    def CanTurnPiece( self ):
        turnedPiece = [[0 for y in range(PIECE_HEIGHT)]
                       for x in range(PIECE_WIDTH)]
        o = (self.orientation + 1) % self.ORIENT_NUM
        turnedPiece = self.piecePattern[self.id][o]
        # 確認
        flag = 0
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                # 添字が範囲を越えているかどうかの判定
                xx = self.location[0] + x
                yy = self.location[1] + y
                if ( xx < 0 or xx >= FIELD_WIDTH ):
                    flag = 1
                    print "turn NG (1)"
                    break
                if ( yy < -int((self.location[0]+x) * 0.5) or
                     yy >= FIELD_HEIGHT - int((self.location[0]+x) * 0.5) ):
                    flag = 1
                    print "turn NG (2)"
                    break
                # 重なっている場合
                if ( turnedPiece[x][y] == 1 and
                     self.field[xx][yy] >= 1 ):
                    flag = 1
                    print "turn NG (3)"
                    break
        if flag == 1:
            return False
        else:
            return True

    # 再描画
    def RedrawAllField( self ):
        # ゲームの終了後は実行しない
        # 開始前は表示する
        if self.game_end == True:
            return
        #print "RedrawAllField"
        # まず固定セル
        for x in range( FIELD_WIDTH ):
            for y in range( - int(x * 0.5), FIELD_HEIGHT - int(x * 0.5) ):
                self.main_board.SetColor( x, y, self.field[x][y] )
        # 次に移動中のpiece
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                xx = x + self.location[0]
                yy = y + self.location[1]
                if xx < 0: pass
                if xx >= FIELD_WIDTH: pass
                if yy < -int(x * 0.5): pass
                if yy >= FIELD_HEIGHT - int(x * 0.5): pass
                # 描かれていない場所まで塗らないようにする
                if self.piece[x][y] == 1:
                    self.main_board.SetColor( xx, yy, 1 )

##------
if __name__ == '__main__':
    f = Frame()
    f.pack()
    f.mainloop()

# EOF

Pythonでゲーム作成(テトリス)

だいぶ前にPythonでテトリスを実装してみたのですが,使わなくなったので公開します.

参考にしたのはhttp://www13.plala.or.jp/kymats/study/game_other/SPACE_TETRIS/st1.html

実際には,ピースのデータ構造のみ参考にして,盤面はtkinterのCanvas上にrectangleを並べると,これがobjectとして後から色を変更できるようになるので,これを使って実装 しました.

画面はこんな感じです.


点数処理も書いたのですが,これだけで結構な分量になったので,最後に点数処理の部分を除いたソースを以下に掲載します.

これを応用して六角形にしたものを「PythonでHextris」に載せておきます.昔X68000で遊んでいたゲームを作ってみました.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Python 2.7.5 on OS X 10.9.2
# by Mitsuharu Arimura 2015/3/14

import Tkinter as Tk
import datetime as dt
import time, random
import sys, os, csv

# ゲームフィールドのサイズ
FIELD_WIDTH = 18
FIELD_HEIGHT = 30

# ブロックのサイズ
PIECE_WIDTH = 4
PIECE_HEIGHT = 4

# 1個のセルのピクセル数
CELL_WIDTH = 20
CELL_HEIGHT = 20

# ゲーム本体の画面 View
class Board( Tk.Canvas ):
    # フィールドのピクセル数
    BOARD_WIDTH = FIELD_WIDTH * CELL_WIDTH
    BOARD_HEIGHT = FIELD_HEIGHT * CELL_HEIGHT
    margin = 2

    def __init__( self, master ):
        self.width = self.BOARD_WIDTH
        self.height = self.BOARD_HEIGHT
        Tk.Canvas.__init__( self, master, relief=Tk.RAISED, bd=self.margin,
                            bg='white',
                            width=self.BOARD_WIDTH, height=self.BOARD_HEIGHT )

        # Canvas上の rectangle の2次元配列
        self.Field = [[0 for y in range(FIELD_HEIGHT)]
                      for x in range(FIELD_WIDTH)]
        offset = self.margin * 2 + 3
        for x in range( FIELD_WIDTH ):
            for y in range( FIELD_HEIGHT ):
                x0 = x * CELL_WIDTH + offset
                y0 = y * CELL_HEIGHT + offset
                r = self.create_rectangle( x0, y0,
                                           x0 + CELL_WIDTH,
                                           y0 + CELL_HEIGHT,
                                           fill='white',
                                           width=0)
                self.Field[x][y]=r

        # PAUSEの文字
        TEXT_font = ('Helvetica','60','bold')
        TEXT_color = 'red'
        self.pause_text = self.create_text( self.BOARD_WIDTH/2, 100,
                                            text='PAUSE',
                                            font=TEXT_font,
                                            fill=TEXT_color,
                                            justify=Tk.CENTER)
        self.HidePauseText()

    def SetColor( self, x, y, value ):
        #print "SetColor:"
        if x < 0: return
        if x >= FIELD_WIDTH: return
        if y < 0: return
        if y >= FIELD_HEIGHT: return
        #print "(%d, %d)=>%d" % (x, y, value)
        if value == 1:
            self.itemconfigure( self.Field[x][y], fill='black')
        elif value == 2:
            self.itemconfigure( self.Field[x][y], fill='gray')
        elif value == 3:
            self.itemconfigure( self.Field[x][y], fill='slate gray')
        else:
            self.itemconfigure( self.Field[x][y], fill='white')

    # PAUSE中にPAUSEの文字を表示する
    def ShowPauseText( self ):
        self.itemconfigure( self.pause_text, state=Tk.NORMAL )

    # PAUSEが終わるときにPAUSEの文字を非表示にする
    def HidePauseText( self ):
        self.itemconfigure( self.pause_text, state=Tk.HIDDEN )

# 次のピースを表示する画面 View
class NextPieceBoard( Tk.Canvas ):
    # 次のピースを表示する場所のサイズ
    NEXT_BOARD_WIDTH = PIECE_WIDTH * CELL_WIDTH
    NEXT_BOARD_HEIGHT = PIECE_HEIGHT * CELL_HEIGHT
    margin = 2
    def __init__( self, master ):
        Tk.Canvas.__init__( self, master, relief=Tk.RAISED, bd=self.margin,
                            bg='white',
                            width=self.NEXT_BOARD_WIDTH,
                            height=self.NEXT_BOARD_HEIGHT )
        self.RectArray = [[0 for y in range(PIECE_HEIGHT)]
                          for x in range(PIECE_WIDTH)]
        #print self.RectArray
        offset = self.margin * 2 + 3
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                x0 = x * CELL_WIDTH + offset
                y0 = y * CELL_HEIGHT + offset
                r = self.create_rectangle( x0, y0,
                                           x0 + CELL_WIDTH,
                                           y0 + CELL_HEIGHT,
                                           fill='white',
                                           width=0)
                self.RectArray[x][y]=r

    def DisplayPiecePattern( self, pattern ):
        p = pattern
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                # print "p[%d][%d] = %d" % (x, y, p[x][y])
                if p[x][y] == 1:
                    # print "p[%d][%d] = red" % (x, y)
                    self.itemconfigure( self.RectArray[x][y], fill='black' )
                else:
                    # print "p[%d][%d] = blue" % (x, y)
                    self.itemconfigure( self.RectArray[x][y], fill='white' )

# ゲーム全体 Controller
class Frame( Tk.Frame ):
    def __init__( self, master=None ):
        Tk.Frame.__init__( self, master )
        self.master.title( 'Tetris' )

        #########################################
        # board frame
        self.bframe = Tk.Frame( self, bd=1, relief=Tk.RIDGE )
        self.bframe.pack( side=Tk.LEFT, padx=10, pady=10 )
        self.board = Board( self.bframe )
        self.board.pack( side=Tk.LEFT, padx=0, pady=0 )

        #########################################
        # frame start
        frame = Tk.Frame( self, bd=1, relief=Tk.RIDGE )
        # exit button
        self.btExit = Tk.Button( frame, text='Exit', command=self.exitGame )
        self.btExit.pack( anchor=Tk.E, padx=10, pady=10 )
        # replay (not start) button
        #self.btReplay = Tk.Button( frame, text='Replay',
        #                           command=self.replayGame )
        #self.btReplay.pack( anchor=Tk.E, padx=10, pady=0 )
        # start button
        self.btStart = Tk.Button( frame, text='Start', command=self.startGame )
        self.btStart.pack( anchor=Tk.E, padx=10, pady=0 )
        # next piece display
        self.next_piece = NextPieceBoard( frame )
        self.next_piece.pack( anchor = Tk.E, padx=10, pady=10 )
        TEXT_font = ('Helvetica','12','italic')
        NUM_font = ('Helvetica','24','bold')
        # time text
        self.time_text = Tk.Label( frame, text='PLAY TIME', font=TEXT_font )
        self.time_text.pack( anchor = Tk.E, padx=10, pady=0 )
        # timer
        self.time_box = Tk.Label( frame, text='00\'00\"', font=NUM_font,
                                  bd=1, relief=Tk.RIDGE )
        self.time_box.pack( anchor = Tk.E, padx=10, pady=0 )
        frame.pack( side=Tk.RIGHT, padx=10, pady=10 )

        # key bind
        self.bind( "", self.keyPressed )
        self.bind( "", self.keyLeft )
        self.bind( "", self.keyRight )
        self.bind( "", self.keyUp )
        self.bind( "", self.keyDown )
        self.focus_set()
                    
        self.tetris = Tetris( self )

        self.processing_down = False
        self.timerEventCount = 0
        self.timerCount = 0

        self.tetris.PrepareStartGame()

    # exit button
    def exitGame( self ):
        self.tetris.playing = False
        sys.exit()

    # start button
    def startGame( self ):
        self.tetris.playing = True
        # ボタンは押せなくする
        self.DisableStartButton()
        #self.DisableReplayButton()
        # カウンタをリセットしてタイマーをスタートする
        self.timerEventCount = 0
        self.timerCount = 0
        # これ以降,timerEventが0.5秒に一回呼び出される
        self.after( 500, self.timerEvent )

    # replay
    def replayGame( self ):
        self.EnableStartButton()
        self.tetris.PrepareStartGame()
        self.timerEventCount = 0
        self.timerCount = 0
        self.DisplayTime( 0, 0 )

    # 0.5秒ごとに呼び出される
    def timerEvent( self ):
        if self.tetris.playing == False:
            return
        if self.tetris.pausing == True:
            return
        self.timerEventCount = self.timerEventCount + 1
        # 0.5秒に1回呼び出されるので2回→1秒に1回の点数アップ
        if self.timerEventCount % 2 == 0:
            self.timerCount = self.timerCount + 1
            self.DisplayTime( self.timerCount / 60, self.timerCount % 60 )
            # print "count %d" % self.timerEventCount
            # print "timer %d" % self.timerCount
        # ブロックを1段落とす
        self.keyDown( None )
        # 0.5秒後に自分を呼び出す
        self.after( 500, self.timerEvent )

    def endGame( self ):
        self.focus_set()
        self.replayGame()

    def DisableExitButton( self ):
        self.btExit.configure( state=Tk.DISABLED )

    def EnableExitButton( self ):
        self.btExit.configure( state=Tk.NORMAL )

    def DisableReplayButton( self ):
        #self.btReplay.configure( state=Tk.DISABLED )
        pass

    def EnableReplayButton( self ):
        #self.btReplay.configure( state=Tk.NORMAL )
        pass

    def DisableStartButton( self ):
        self.btStart.configure( state=Tk.DISABLED )

    def EnableStartButton( self ):
        self.btStart.configure( state=Tk.NORMAL )

    def DisplayTime( self, min=0, sec=0 ):
        self.time_box.configure( text="%02d\'%02d\"" % (min, sec) )

    # 以下はキー入力処理
    def keyPressed( self, event ):
        c = event.char
        # space: pause
        if self.tetris.playing == False:
            return
        if c == ' ':
            if self.tetris.pausing == False:
                print "PAUSE!!!"
                self.tetris.pausing = True
                # PAUSEを表示
                self.board.ShowPauseText()
            else:
                print "PAUSE END!!!"
                self.tetris.pausing = False
                # PAUSEを隠す
                self.board.HidePauseText()
                # 再度タイマーを開始
                self.after( 500, self.timerEvent )

    def keyLeft( self, event ):
        if self.tetris.playing == False:
            return
        if self.tetris.pausing == True:
            return
        # print "press Left"
        if self.tetris.CanMovePiece( move='left' ) == 0:
            self.tetris.MovePiece( move='left' )
            self.tetris.RedrawAllField()

    def keyRight( self, event ):
        if self.tetris.playing == False:
            return
        if self.tetris.pausing == True:
            return
        # print "press Right"
        if self.tetris.CanMovePiece( move='right' ) == 0:
            self.tetris.MovePiece( move='right' )
            self.tetris.RedrawAllField()

    # キーで呼ばれるのとタイマーで呼ばれるのが同時に重なる場合が
    # あるので再入防止している
    def keyDown( self, event ):
        if self.tetris.playing == False:
            return
        if self.tetris.pausing == True:
            return
        # print "press Down"
        if self.processing_down == True:
            return
        self.processing_down = True
        res = self.tetris.CanMovePiece( move='down' )
        if res == 0:
            self.tetris.MovePiece( move='down' )
            self.tetris.RedrawAllField()
        elif res == 2:
            self.tetris.FixPieceAndGetNextPiece()
        self.processing_down = False

    # 回転
    def keyUp( self, event ):
        if self.tetris.playing == False:
            return
        if self.tetris.pausing == True:
            return
        # print "press Up"
        if self.tetris.CanTurnPiece() == True:
            self.tetris.TurnPiece()
            self.tetris.RedrawAllField()

# テトリスゲームの Data
class Tetris:
    # ゲームフィールド
    field = [[0 for y in range(FIELD_HEIGHT)]
             for x in range(FIELD_WIDTH)]
    # 現在移動中のブロック
    piece = [[0 for y in range(PIECE_HEIGHT)]
             for x in range(PIECE_WIDTH)]
    # ブロックの左上端の座標(フィールドは左上が(x,y) = (0,0)でyが下向き)
    location = [0, 0]
    # 次のブロック
    nextPiece = [[0 for y in range(PIECE_HEIGHT)]
                 for x in range(PIECE_WIDTH)]
    # ブロックの全てのパターン
    piecePattern = [
        [[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]], # 0
        [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], # 1
        [[0,0,0,0],[0,1,1,1],[0,0,1,0],[0,0,0,0]], # 2
        [[0,0,0,0],[0,1,1,1],[0,1,0,0],[0,0,0,0]], # 3
        [[0,0,0,0],[0,1,0,0],[0,1,1,1],[0,0,0,0]], # 4
        [[0,0,0,0],[0,0,1,1],[0,1,1,0],[0,0,0,0]], # 5
        [[0,0,0,0],[0,1,1,0],[0,0,1,1],[0,0,0,0]]] # 6

    def __init__( self, parent ):
        self.main_frame = parent
        self.main_board = parent.board
        self.next_piece = parent.next_piece

    # ゲームの開始準備
    def PrepareStartGame( self ):
        self.playing = False
        self.pausing = False
        self.game_end = False

        # 盤面の初期化
        # 全部を白で塗る
        for y in range( FIELD_HEIGHT ):
            for x in range( FIELD_WIDTH ):
                self.field[x][y] = 0
        # 周囲1列を3で塗る
        for y in range( FIELD_HEIGHT ):
            self.field[0][y] = 3
            self.field[ FIELD_WIDTH - 1 ][y] = 3
        for x in range( FIELD_WIDTH ):
            self.field[x][ FIELD_HEIGHT - 1 ] = 3

        # 障害物を置いておく
        self.field[1][22] = 2
        self.field[2][22] = 2
        self.field[3][21] = 2
        self.field[4][21] = 2
        self.field[5][20] = 2
        self.field[6][20] = 2

        # 1個目のピースを準備する
        self.GetNextPiece( True )
        self.RedrawAllField()

    # 1行だけ消す処理
    def DeleteOneLine( self, line ):
        # 上から落とす
        for y in range(line, 1, -1):
            for x in range(1, FIELD_WIDTH-1):
                self.field[x][y] = self.field[x][y-1]
        self.RedrawAllField()

    # 埋まっている行を全て消す処理
    def DeleteFilledLines( self ):
        #print "Start Delete Line!!!"
        y = FIELD_HEIGHT - 2 # 一番下の行を除く
        deleted_line = 0 # 削除できた行の数
        all_clear = 0
        # 一番上の行まで確認
        count_sum = 0
        while y >= 0:
            # 1行に埋まってるセルの個数を数える
            #print "count line %d:" % y,
            count = 0
            for x in range(1, FIELD_WIDTH-1): # 両端を除く
                if self.field[x][y] == 2:
                    count = count + 1
            #print "count %d" % count
            # 全部が埋まっていたら行の削除処理
            if count == FIELD_WIDTH - 2: # 両端を除いた数
                # 削除できた場合にはyは増えない
                print "clear line %d" % y
                self.DeleteOneLine(y)
                deleted_line = deleted_line + 1
            else:
                count_sum = count_sum + count
                # 全部が埋まっていなかったら次の行へ行く
                y = y - 1
        print "end Delete Line!!! (%d lines)" % deleted_line
        print "%d cells left" % count_sum

        # count_sumが0だったらボーナス
        if count_sum == 0:
            all_clear == 1
            print '########################################'
            print '######## ALL CELLS CLEARED #############'
            print '########################################'

    # 現在のピースが落ち切ったので,ここに固定する
    # 行が埋まったら消す
    # 次のピースを開始する
    def FixPieceAndGetNextPiece( self ):
        # 現在のピースを現在の位置に固定する
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                if self.piece[x][y] == 1:
                    self.field[self.location[0]+x][self.location[1]+y] = 2
        # 埋まっている行を消す
        self.DeleteFilledLines()
        # 次のピース
        self.GetNextPiece( False )
        self.RedrawAllField()

    # next pieceの領域に次のピースを作成する
    def CreatePiece( self ):
        i = random.randint(0, len(self.piecePattern)-1)
        # print "[%d]" % i,
        self.nextPiece = self.piecePattern[i]
        # print "-->", self.nextPiece
        self.next_piece.DisplayPiecePattern( self.nextPiece )

    # next pieceの領域からピースを持ってきて一番上に置く
    # 既にここまでピースが積んであったらゲームオーバー判定
    def GetNextPiece( self, is_first ):
        if is_first:
            self.CreatePiece()
        #print self.nextPiece
        self.location[0] = 5
        self.location[1] = 0
        for y in range( PIECE_HEIGHT ):
            for x in range( PIECE_WIDTH ):
                # 次のピースを持ってくる
                self.piece[x][y] = self.nextPiece[x][y]
                # フィールドに置く
                self.main_board.SetColor( self.location[0]+x,
                                          self.location[1]+y,
                                          self.piece[x][y])
        # ゲーム終了判定
        for y in range( PIECE_HEIGHT ):
            # 内側のxのloopから抜けて来ていたら,ここでも抜ける
            if self.game_end == True:
                break
            for x in range( PIECE_WIDTH ):
                # フィールドに置いた瞬間,既にその場所にブロックがあったら
                if ( self.piece[x][y] == 1 and
                     self.field[self.location[0]+x][self.location[1]+y] == 2):
                    # 終わり
                    self.game_end = True
                    # タイマーでのloopを止める
                    self.playing = False
                    # 1個重なっていれば十分なので,ここで抜ける
                    break
        if self.game_end == False:
            # 終わっていなかったら続ける
            self.CreatePiece()
        else:
            # ゲームオーバー処理の開始
            print "GAME END !!!"
            self.main_frame.endGame()

    def MoveOrientation( self, move ):
        xmove = ymove = 0
        if move == 'down':
            ymove = 1
        elif move == 'left':
            xmove = -1
        elif move == 'right':
            xmove = 1
        return ( xmove, ymove )

    def MovePiece( self, move ):
        ( xmove, ymove ) = self.MoveOrientation( move )
        self.location[0] = self.location[0] + xmove
        self.location[1] = self.location[1] + ymove
        
    def TurnPiece( self ):
        turnedPiece = [[0 for y in range(PIECE_HEIGHT)]
                       for x in range(PIECE_WIDTH)]
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                turnedPiece[y][PIECE_HEIGHT-1-x] = self.piece[x][y]
        self.piece = turnedPiece

    # 移動した先を計算して,fieldとかぶらないか,はみ出ないかを見る
    # return 1: 左右の壁に当たって移動できない
    # return 2: 積んであるフィールド上のセルと重なるか,
    #           下に当たっているので,これで固定する
    def CanMovePiece( self, move='down' ):
        ( xmove, ymove ) = self.MoveOrientation( move )
        ( x0, y0 ) = ( self.location[0], self.location[1] )
        # フィールド上のセルと重なっている場合
        flag = 0
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                if ( self.piece[x][y] == 1 and
                     self.field[x0+x+xmove][y0+y+ymove] >= 1 ):
                    flag = 1
                    #print "move NG (4) (%d,%d)->(%d,%d)" % (
                    #    self.location[0]+x, self.location[1]+y,
                    #    self.location[0]+x+xmove, self.location[1]+y+ymove )
                    break
        if flag == 1:
            if ymove == 1:
                return 2 # 下に動けない場合はこれで固定
            else:
                return 1 # 左右ぶつかっている場合は,動けないだけ
        # 以上のチェックに引っかからない場合は動ける
        return 0

    # 回転した先を計算して,fieldとかぶらないかを見る
    # True: 回転できる
    def CanTurnPiece( self ):
        turnedPiece = [[0 for y in range(PIECE_HEIGHT)]
                       for x in range(PIECE_WIDTH)]
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                turnedPiece[y][PIECE_HEIGHT-1-x] = self.piece[x][y]
        # 確認
        flag = 0
        for x in range(PIECE_WIDTH):
            for y in range(PIECE_HEIGHT):
                # 添字が範囲を越えているかどうかの判定
                xx = self.location[0] + x
                yy = self.location[1] + y
                if ( xx < 0 or xx >= FIELD_WIDTH ):
                    flag = 1
                    print "turn NG (1)"
                    break
                if ( yy < 0 or yy >= FIELD_HEIGHT ):
                    flag = 1
                    print "turn NG (2)"
                    break
                # 重なっている場合
                if ( turnedPiece[x][y] == 1 and
                     self.field[xx][yy] >= 1 ):
                    flag = 1
                    print "turn NG (3)"
                    break
        if flag == 1:
            return False
        else:
            return True

    # 再描画
    def RedrawAllField( self ):
        # ゲームの終了後は実行しない
        # 開始前は表示する
        if self.game_end == True:
            return
        #print "RedrawAllField"
        # まず固定セル
        for x in range( FIELD_WIDTH ):
            for y in range( FIELD_HEIGHT ):
                self.main_board.SetColor( x, y, self.field[x][y] )
        # 次に移動中のpiece
        for x in range( PIECE_WIDTH ):
            for y in range( PIECE_HEIGHT ):
                xx = x + self.location[0]
                yy = y + self.location[1]
                if xx < 0: pass
                if xx >= FIELD_WIDTH: pass
                if yy < 0: pass
                if yy >= FIELD_HEIGHT: pass
                # 描かれていない場所まで塗らない
                if self.piece[x][y] == 1:
                    self.main_board.SetColor( xx, yy, self.piece[x][y] )

##------
if __name__ == '__main__':
    f = Frame()
    f.pack()
    f.mainloop()

# EOF