#!/usr/bin/python

#from ncurses.curses import *
from curses import *
import time, traceback, whrandom, copy, socket, sys, getopt
import string, pickle, os, pwd

def make_tensor(*args):
    """
    create tensor, i.e. list of lists
    based on posting by Stephan Houben
    """
    if args:
        head = args[0]
        tail = args[1:]
        result = []
        for i in range(head):
            result.append(apply(make_tensor, tail))
        return result
    else:
        return 0

def mybeep():
    if globalvar.beep:
        beep()


def savescore():
    try:
        f = open(globalvar.scorefile, "w")
        pickle.dump(globalvar.score, f)
        f.close()
    except:
        pass

def loadscore():
    try:
        f = open(globalvar.scorefile, "r")
        globalvar.score = pickle.load(f)
        f.close()
    except:
        pass


class Globalvar:

    def __init__(self):
        self.zoom = (1, 2)
        self.colours = 1
        self.inverse = 0
        self.ascii = 1
        self.myport = 5634
        self.otherhost = "localhost"
        self.otherport = 5635
        self.xsize = 16
        self.ysize = 8
        self.pieces = "basic"
        self.nlevel = 3
        self.speedup = 0.988
        self.wait = 1
        self.kpract = kpract
        self.pushing = 1
        self.happyholik = 0
        self.beep = 1
        self.flip = 0
        self.scorefile = "/var/games/pytris.score"
        self.score = {0:{'Loosers':[], 'Winners':[]}}
        self.next = 0
        self.shadow = 1


class Display:

    def __init__(self, stdscr):

        # initialize curses - be sure to assign stdscr to a variable to prevent it
        # from being immediately garbage collected.
        self.stdscr = stdscr
        if globalvar.colours:
            start_color() # turn on color mode
        cbreak() # provide unbuffered input
        noecho() # turn off input echoing
        nonl() # turn off newline translation
        intrflush(0) # turn off flush-on-interrupt
        curs_set(0)
    
        self.one = newwin(0, globalvar.zoom[1]*globalvar.ysize+2, 0, 0)
        self.two = newwin(0, globalvar.zoom[1]*globalvar.ysize+2, 0, globalvar.zoom[1]*globalvar.ysize+2)
        self.info = newwin(5, 13, 0, globalvar.zoom[1]*globalvar.ysize*2+4)
        self.next = newwin(4+4*globalvar.next*globalvar.zoom[0], 13,
                           5, globalvar.zoom[1]*globalvar.ysize*2+4)
        self.one.leaveok(1)
        self.two.leaveok(1)
        self.info.leaveok(1)
        self.next.leaveok(1)
        self.one.keypad(1) # turn on keypad mode
        init_pair(1, COLOR_YELLOW, COLOR_GREEN)
        init_pair(2, COLOR_CYAN, COLOR_RED)
        init_pair(3, COLOR_RED, COLOR_CYAN)
        init_pair(4, COLOR_GREEN, COLOR_YELLOW)
        init_pair(5, COLOR_BLUE, COLOR_CYAN)
        init_pair(6, COLOR_CYAN, COLOR_MAGENTA)
        init_pair(7, COLOR_BLUE, COLOR_RED)
        self.one.box()
        self.two.box()
        self.info.box()
        self.info.move(1,1)
        self.info.addstr("Score:")
        self.next.box()
        self.next.move(1,1)
        self.next.addstr("Next:")
        self.two.nodelay(1)
        self.one.nodelay(1)
        self.info.noutrefresh()
        self.next.noutrefresh()
        self.two.noutrefresh()
        self.one.noutrefresh() # copy window to virtual screen, don't update real screen

        self.LINES, self.COLS = self.stdscr.getmaxyx()

    def plotsquare(self, player, x, y, par):
        # plot square, x, y are internal coords - translates them to ncurses
        scr = self.one
        if player == 2:
            x=-x
        cy = (y-1)*globalvar.zoom[1]+1
        for i in range(globalvar.zoom[0]):
            if player == 3: # next piece
                cx = -x*globalvar.zoom[0]+i
                scr = self.next
            else:
                cx = (game.xsize-x)*globalvar.zoom[0]+i
                if cx >= self.LINES-1:
                    cx = cx-self.LINES+2
                    scr = self.two
            if cx > 0:
                scr.move(cx, cy)
                if par:
                    if globalvar.ascii:
                        p = pieces.sqchars[par]
                    else:
                        p = " "
                    if globalvar.inverse:
                        attr = A_REVERSE
                    else:
                        attr = A_NORMAL
                    scr.addstr(p*globalvar.zoom[1], attr | color_pair(pieces.sqcolours[par]))
#                    scr.chgat(globalvar.zoom[1], attr, pieces.sqcolours[par])
                else:
                    scr.addstr(" "*globalvar.zoom[1])

    def refresh(self):
        self.two.noutrefresh()
        self.one.noutrefresh()
        doupdate() # update screen
        
    def drawsep(self, c):
        cx = game.xsize*globalvar.zoom[0]
        scr = self.one
        if cx >= self.LINES-1:
            cx = cx - self.LINES+2
            scr = self.two
        scr.move(cx, 1)
        scr.addstr(c*game.ysize*globalvar.zoom[1])
        cx = globalvar.xsize*2*globalvar.zoom[0]
        scr = self.one
        if cx >= display.LINES-1:
            cx = cx - display.LINES+2
            scr = self.two
        if cx < display.LINES-1:
            scr.move(cx, 1)
            scr.addstr(c*game.ysize*globalvar.zoom[1])

    def redisplay(self, bl, hisbl):
        #if bl: self.drawsep(" ")
        for i in range(1, bl):
            for j in range(1, game.ysize+1):
                p = game.matrix[i-1][j-1]
                if p:
                    s = p
                else:
                    s = None
                self.plotsquare(1, i, j, s)
        for i in range(1, hisbl):
            for j in range(1, game.ysize+1):
                try:
                    p = game.hismatrix[i-1][j-1]
                except IndexError:
                    p = None
                if p:
                    s = p
                else:
                    s = None
                self.plotsquare(2, i, j, s)
        #if bl: self.drawsep("=")
        self.displayscore()

    def displayscore(self):
        self.info.move(2, 1)
        self.info.addstr("%6i" % game.score)
        self.info.move(3, 1)
        self.info.addstr("%6i" % game.ticks)


    def shownext(self):
        self.next.erase()
#        self.next.line_border()
        self.next.move(1,1)
        self.next.addstr("Next:")
        for i in range(globalvar.next):
            for j in pieces.rpool[i]:
                display.plotsquare(3, -4-i*globalvar.zoom[0]*4+j[0], 3+j[1], j[2])
        self.next.refresh()
        self.info.refresh()

    def showhighscores(self, opt):
        for j in 'Winners', 'Loosers':
            if j == 'Winners':
                pos = 1
            else:
                pos = self.COLS/2
            display.stdscr.move(0, pos)
            display.stdscr.addstr(j+":")
            for i in range(min(30, self.LINES-2,len(globalvar.score[opt][j]))):
                display.stdscr.move(i+2, pos)
                a = globalvar.score[opt][j][i]
                s = a[1]
                mls = self.COLS/2-(4+5+4)
                s = string.ljust(s[:mls], mls)
                attr = A_NORMAL
                if a[2]: # current entry
                    attr = A_BOLD
                display.stdscr.addstr("%2i. %s %5i  " % (i+1, s, a[0]), attr) 
#                if a[2]: # current entry
#                    display.stdscr.move(i+2, pos)
#                    display.stdscr.chgat(28, A_BOLD, 0)

    def showinmiddle(self, s):
        display.stdscr.move(self.LINES/2, (self.COLS-len(s))/2)
        display.stdscr.addstr(s) # output string
        display.stdscr.noutrefresh()
        doupdate()

    def __del__(self):
        echo()
        nl()
        curs_set(1)
      
      

class Display1:

    def __init__(self):

        # initialize curses - be sure to assign stdscr to a variable to prevent it
        # from being immediately garbage collected.
        self.stdscr = initscr()
        if globalvar.colours:
            start_color() # turn on color mode
        cbreak() # provide unbuffered input
        noecho() # turn off input echoing
        nonl() # turn off newline translation
        intrflush(FALSE) # turn off flush-on-interrupt
        curs_set(0)
    
        self.one = WINDOW(0, globalvar.zoom[1]*globalvar.ysize+2, 0, 0)
        self.two = WINDOW(0, globalvar.zoom[1]*globalvar.ysize+2, 0, globalvar.zoom[1]*globalvar.ysize+2)
        self.info = WINDOW(5, 13, 0, globalvar.zoom[1]*globalvar.ysize*2+4)
        self.next = WINDOW(4+4*globalvar.next*globalvar.zoom[0], 13,
                           5, globalvar.zoom[1]*globalvar.ysize*2+4)
        self.one.leaveok(TRUE)
        self.two.leaveok(TRUE)
        self.info.leaveok(TRUE)
        self.next.leaveok(TRUE)
        self.one.keypad(TRUE) # turn on keypad mode
        init_pair(1, COLOR_YELLOW, COLOR_GREEN)
        init_pair(2, COLOR_CYAN, COLOR_RED)
        init_pair(3, COLOR_RED, COLOR_CYAN)
        init_pair(4, COLOR_GREEN, COLOR_YELLOW)
        init_pair(5, COLOR_BLUE, COLOR_CYAN)
        init_pair(6, COLOR_CYAN, COLOR_MAGENTA)
        init_pair(7, COLOR_BLUE, COLOR_RED)
        self.one.line_border()
        self.two.line_border()
        self.info.line_border()
        self.info.move(1,1)
        self.info.addchstr("Score:")
        self.next.line_border()
        self.next.move(1,1)
        self.next.addchstr("Next:")
        self.two.nodelay(1)
        self.one.nodelay(1)
        self.info.noutrefresh()
        self.next.noutrefresh()
        self.two.noutrefresh()
        self.one.noutrefresh() # copy window to virtual screen, don't update real screen

        self.LINES = LINES()

    def plotsquare(self, player, x, y, par):
        # plot square, x, y are internal coords - translates them to ncurses
        scr = self.one
        if player == 2:
            x=-x
        cy = (y-1)*globalvar.zoom[1]+1
        for i in range(globalvar.zoom[0]):
            if player == 3: # next piece
                cx = -x*globalvar.zoom[0]+i
                scr = self.next
            else:
                cx = (game.xsize-x)*globalvar.zoom[0]+i
                if cx >= display.LINES-1:
                    cx = cx-display.LINES+2
                    scr = self.two
            if cx > 0:
                scr.move(cx, cy)
                if par:
                    if globalvar.ascii:
                        p = pieces.sqchars[par]
                    else:
                        p = " "
                    scr.addchstr(p*globalvar.zoom[1])
                    if globalvar.inverse:
                        attr = A_REVERSE
                    else:
                        attr = A_NORMAL
                    scr.chgat(globalvar.zoom[1], attr, pieces.sqcolours[par])
                else:
                    scr.addchstr(" "*globalvar.zoom[1])

    def refresh(self):
        self.two.noutrefresh()
        self.one.noutrefresh()
        doupdate() # update screen
        
    def drawsep(self, c):
        cx = game.xsize*globalvar.zoom[0]
        scr = self.one
        if cx >= display.LINES-1:
            cx = cx - display.LINES+2
            scr = self.two
        scr.move(cx, 1)
        scr.addchstr(c*game.ysize*globalvar.zoom[1])
        cx = globalvar.xsize*2*globalvar.zoom[0]
        scr = self.one
        if cx >= display.LINES-1:
            cx = cx - display.LINES+2
            scr = self.two
        scr.move(cx, 1)
        scr.addchstr(c*game.ysize*globalvar.zoom[1])

    def redisplay(self, bl, hisbl):
        #if bl: self.drawsep(" ")
        for i in range(1, bl):
            for j in range(1, game.ysize+1):
                p = game.matrix[i-1][j-1]
                if p:
                    s = p
                else:
                    s = None
                self.plotsquare(1, i, j, s)
        for i in range(1, hisbl):
            for j in range(1, game.ysize+1):
                try:
                    p = game.hismatrix[i-1][j-1]
                except IndexError:
                    p = None
                if p:
                    s = p
                else:
                    s = None
                self.plotsquare(2, i, j, s)
        #if bl: self.drawsep("=")
        self.displayscore()

    def displayscore(self):
        self.info.move(2, 1)
        self.info.addchstr("%6i" % game.score)
        self.info.move(3, 1)
        self.info.addchstr("%6i" % game.ticks)


    def shownext(self):
        self.next.erase()
        self.next.line_border()
        self.next.move(1,1)
        self.next.addchstr("Next:")
        for i in range(globalvar.next):
            for j in pieces.rpool[i]:
                display.plotsquare(3, -4-i*globalvar.zoom[0]*4+j[0], 3+j[1], j[2])
        self.next.refresh()
        self.info.refresh()



class DummyNet:

    def sendmsg(self, msg, level):
        pass

    def receivemsg(self):
        return None


class Net:

    def __init__(self):
        self.outsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.outsock.bind(('', 0))
        self.insock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.insock.setblocking(0)
        self.insock.bind(('', globalvar.myport))
        self.netbuf = ""

    def sendmsg(self, msg, level):
        if level <= globalvar.nlevel:
            try:
                self.outsock.sendto(msg+"\n", (globalvar.otherhost, globalvar.otherport))
            except socket.error, x:
                pass

    def receivemsg(self):
        try:
            data, addr = self.insock.recvfrom(4096)
        except socket.error, x:
            if x[0] == 11:
                return None
            print "Network error:", x
            return None
        return data
 



class Game:

    def __init__(self):

        self.xsize = globalvar.xsize
        self.ysize = globalvar.ysize
        # size of canvas. xsize is vertical, ysize horizontal
        self.matrix = make_tensor(self.xsize*2, self.ysize)
        self.sleeptime = 0.04
        self.bottomline = 0  # how high does the bottom reach
        self.hisbottomline = 0
        self.score = 0
        self.hisscore = 0
        self.hisnmb = 255 # to control if his piece is new one
        self.nmb = 0
        self.ticks = 15


    def makebottom(self):
        #self.sleeptime = self.sleeptime*globalvar.speedup
        self.ticks = self.ticks * globalvar.speedup
        if self.bottomline >= self.xsize:  # end of game - lost
            return 3
        found = 0
        for i in range(1, self.bottomline+1):
            while not (0 in self.matrix[i-1]): # full line
                for j in range(i, self.bottomline+1):
                    self.matrix[j-1] = self.matrix[j]
                found = found + 1

        if found:
            self.bottomline = self.bottomline - found
            self.matrix[self.bottomline:self.bottomline+found+1] = make_tensor(found+1, self.ysize)
            self.score = self.score + 8*found
            self.xsize = min(2*globalvar.xsize, self.xsize + found*globalvar.pushing)
            net.sendmsg("X%i %.5f" % (self.xsize, self.ticks), 1)
            if self.xsize == 2*globalvar.xsize:  # end of game - victory
                return 4
        mtob = []
        for i in range(self.bottomline):
            mtob.append(self.matrix[i])
        net.sendmsg("B" + string.replace(`[self.bottomline]+mtob`, " ", ""), 2)
        g = max(0, globalvar.pushing)
        display.redisplay(self.bottomline+found*(g+1)+1, self.hisbottomline+2)
        
        return 2
                    
        
                
class Pieces:

    def __init__(self, t):

        self.sqchars = " #$%@*+&"
        self.sqcolours = (0,1,2,3,4,5,6,7)
        masks = {}
        masks["tetris"] = ( # (xpos, ypos, type)
                     ((0,-1,7), (0,0,7), (0,1,7), (0,2,7)),
                     ((-1,1,2), (-1,0,2), (0,0,2), (0,1,2)),
                     ((1,-1,3), (1,0,3), (0,0,3), (0,1,3)),
                     ((-1,-1,4), (-1,0,4), (-1,1,4), (0,1,4)),
                     ((0,-1,5), (0,0,5), (0,1,5), (1,0,5)),
                     ((-1,1,6), (0,1,6), (1,0,6), (0,0,6)),
                     ((0,-1,1), (0,0,1), (0,1,1), (-1,1,1))
                    )
        masks["basic"] = masks["tetris"]

        masks["monotris"] = ( ((0,0,1),), )
        masks["ditris"] = ( ((0,0,1), (0,1,1)) ,)
                    
        masks["tritris"] = (
                     ((0,-1,1), (0,0,1), (0,1,1)),
                     ((-1,0,2), (-1,1,2), (0,1,2)),
                     ((1,0,3), (1,1,3), (0,1,3))
                    )
                    
        masks["simple"] = masks["tritris"]+masks["monotris"]+masks["ditris"]
        
        masks["pentris"] = (
                     ((0,-2,7),(0,-1,7), (0,0,7), (0,1,7), (0,2,7)),
                     ((-1,1,2), (-1,0,2), (0,0,2), (0,1,2),(0,2,2)),
                     ((-1,1,1), (-1,0,1), (0,0,1), (0,1,1),(-1,2,1)),
                     ((1,-1,3), (1,0,3), (0,0,3), (0,1,3),(0,2,3)),
                     ((1,-1,3), (1,0,3), (0,0,3), (0,1,3),(-1,1,3)),
                     ((1,-1,4), (1,0,4), (0,0,4), (0,1,4),(2,-1,4)),
                     ((-1,-1,4), (-1,0,4), (-1,1,4), (0,1,4),(0,2,4)),
                     ((-1,-1,4), (-1,0,4), (-1,1,4), (0,1,4),(-1,-2,4)),
                     ((0,-1,5), (0,0,5), (0,1,5), (1,0,5),(-1,0,5)),
                     ((0,-1,5), (0,0,5), (0,1,5), (1,0,5),(0,2,5)),
                     ((0,-1,6), (0,0,6), (0,1,6), (1,0,6),(0,-2,6))
                    )

        masks["extended"] = masks["simple"]+masks["tetris"] + \
                (
                     ((0,0,1),),
                     ((0,-1,2), (0,0,2)),
                     ((1,-1,3), (0,-1,3), (0,0,3), (0,1,3), (1,1,3)),
                     ((1,-1,4), (0,-1,4), (1,0,4), (0,1,4), (1,1,4))
                )
        masks["crazy"] = masks["extended"] + masks["pentris"] + \
                (
                     ((0,-1,1), (0,0,1), (0,2,1)),
                     ((-1,0,2), (-1,1,2), (0,-1,2)),
                     ((1,0,3), (1,1,3), (0,-1,3)),
                     ((-1,-1,3), (0,0,3), (1,1,3)),
                     ((0,-1,4), (0,1,4), (1,0,4)),
                     ((0,-1,2), (0,0,2), (1,1,2), (1,2,2)),
                     ((1,-1,1), (1,0,1), (0,1,1), (0,2,1)),
                     ((-1,0,4), (0,1,4), (1,0,4), (0,-1,4)),
                     ((-1,1,7), (1,1,7), (1,-1,7), (-1,-1,7)),
                     ((-1,-1,5), (0,0,5), (1,1,5), (-1,1,5), (1,-1,5)),
                     ((-1,-1,6), (-1,0,6), (-1,1,6), (0,-1,6), (0,1,6), (1,-1,6), (1,0,6), (1,1,6), (1,-1,6))
                )
                    
        self.masks = masks[t]
        self.rpool = map(lambda x, self=self:whrandom.choice(self.masks), (globalvar.next+1)*[None]) # pool of pieces
                    
    def getpiece(self):
        p = self.rpool[0]
        del self.rpool[0]
        self.rpool.append(whrandom.choice(self.masks))
        return p



class HisPiece:
    def __init__(self, mask=[]):
        self.x = game.xsize
        self.y = game.ysize/2
        self.mask = mask

    def draw(self):
        for i in self.mask:
            display.plotsquare(2, self.x + i[0], self.y + i[1], i[2])
    
    def undraw(self):
        for i in self.mask:
            display.plotsquare(2, self.x + i[0], self.y + i[1], None)


class Piece:

    def __init__(self, mask):
        self.mask = mask
        if globalvar.happyholik:
            self.height = max(map(lambda x: x[0], self.mask))
            self.x = game.xsize-self.height
        else:
            self.x = game.xsize
        self.y = game.ysize/2
        self.tickc = game.ticks
        self.nmb = game.nmb + 1
        if self.nmb > 255:
            self.nmb = 0
        game.nmb = self.nmb

    def tick(self, tickv=1):
        self.tickc = self.tickc - tickv
        if self.tickc <= 0:
            self.tickc = game.ticks
            r = self.down()
        else:
            r = 0
        return r
        
    def down(self):
        global game
        if self.x > game.xsize:
            self.x = game.xsize
        r = self.validpos(self.x-1, self.y, self.mask)
        if r == 1: # ok, go down
            self.undraw()
            self.x = self.x - 1
            self.draw()
        elif r == 2: # we hit the bottom
            for i in self.mask:
                try:
                    game.matrix[self.x+i[0]-1][self.y+i[1]-1] = i[2]
                    game.bottomline = max(game.bottomline, self.x+i[0])
                    game.score = game.score + 1
                    display.displayscore()
                except:
                    return 3 # end of game
            r = game.makebottom()
        return r
        
    def left(self):
        if self.validpos(self.x, self.y-1, self.mask) == 1:
            self.undraw()
            self.y = self.y - 1
            self.draw()

    def right(self):
        if self.validpos(self.x, self.y+1, self.mask) == 1:
            self.undraw()
            self.y = self.y + 1
            self.draw()

    def rotate(self):
        self.nmask = []
        for i in self.mask:
            self.nmask.append((i[1], -i[0], i[2]))
        if self.validpos(self.x, self.y, self.nmask) == 1:
            self.undraw()
            self.mask = self.nmask
            self.draw()

    def flip(self):
        self.nmask = []
        if globalvar.flip == 1:
            for i in self.mask:
                self.nmask.append((-i[0], i[1], i[2]))
        elif globalvar.flip == 2:
            for i in self.mask:
                self.nmask.append((i[0], -i[1], i[2]))
        if self.validpos(self.x, self.y, self.nmask) == 1:
            self.undraw()
            self.mask = self.nmask
            self.draw()


    def drop(self):
        for j in range(self.x, -1, -1):
            r = self.validpos(j, self.y, self.mask)
            if r <> 1:
                self.undraw()
                self.x = j+1
                r = self.down()
                return r
    
            
    def draw(self):
        net.sendmsg("P"+chr(self.nmb)+"%i %i " % (self.x, self.y)  + string.replace(`self.mask`, " ", ""), 3)
        for i in self.mask:
            display.plotsquare(1, self.x + i[0], self.y + i[1], i[2])
            if globalvar.shadow == 1:
                display.plotsquare(1, 0, self.y+i[1], 0)
            elif globalvar.shadow == 2:
                display.plotsquare(1, 0, self.y+i[1], i[2])
        display.refresh()
    
    def undraw(self):
        for i in self.mask:
            display.plotsquare(1, self.x + i[0], self.y + i[1], None)
        display.drawsep("=")
        
    def validpos(self, x, y, mask):
        "test if x, y, mask is a valid position"
        for i in mask:
            if  y+i[1] < 1 or y+i[1] > game.ysize:
                return 0
            if x+i[0] < 1:
                return 2
            if x+i[0] > game.xsize:
                return 1
            #if x+i[0]-1 > game.bottomline:
            #    return 1
            if game.matrix[x+i[0]-1][y+i[1]-1]:
                return 2
        return 1
        
        
def actonmsg(m):

    if not m:
        return 1
    if m[-1:] <> '\n':
        print "Unexpected packet"
        return 1
    t = m[0]
    if t == 'L': # our victory
        return 4
    elif t == 'V': # our lost
        return 3
    elif t == 'X': # change in xsize
        s = string.split(m[1:-1]) # his size
        ns = globalvar.xsize*2 - int(s[0])
        game.piece.undraw()
        game.hispiece.undraw()
        game.hisnmb = -1
        diff = max(0, game.xsize-ns)
        game.xsize = ns
        game.ticks = float(s[1]) # synchronize speed as well
        if diff >0:
            game.piece.down()
        display.redisplay(game.bottomline+2, game.hisbottomline+2+diff)
        #game.piece.draw()
        return 0
    elif t == 'B': # bottom
        hism = eval(m[1:-1])
        nhb = hism[0]
        game.hismatrix[:nhb] = hism[1:]
        game.hismatrix[nhb:] = make_tensor(globalvar.xsize-nhb, game.ysize)
        nhmax = max(nhb, game.hisbottomline)
        game.hisbottomline = nhb
        game.hispiece.undraw()
        display.redisplay(0, nhmax+2)
    elif t == 'P': # piece
        nmb = ord(m[1])
        s = string.split(m[2:-1], " ", 2)
        nx = int(s[0])
        game.hispiece.undraw()
        #display.redisplay(0, game.hisbottomline+2)
        if nmb == game.hisnmb: # the same piece
            if nx <= game.hispiece.x: # but only if received in order
                game.hispiece.x = nx
                game.hispiece.y = int(s[1])
                game.hispiece.mask = eval(s[2])
                game.hispiece.draw()
        else:
            #display.redisplay(0, game.hisbottomline+2)
            game.hisnmb = nmb
            game.hispiece.x = nx
            game.hispiece.y = int(s[1])
            game.hispiece.mask = eval(s[2])
            game.hispiece.draw()
    else:
        return 1


def kpract(a):
    "read pressed key and act on it"
    tickv = 1
    r = 1
    kpr = display.one.getch()
    
    if kpr == KEY_LEFT:
        kpr = ord('u')
    elif kpr == KEY_RIGHT:
        kpr = ord('o')
    elif kpr == KEY_UP:
        kpr = ord('i')
    elif kpr == KEY_DOWN:
        kpr = ord('m')
        
    if kpr == ord('u'):
        a.left()
    elif kpr == ord('o'):
        a.right()
    elif kpr == ord('i'):
        a.rotate()
    elif kpr == ord('x') and globalvar.flip:
        a.flip()
    elif kpr == ord('m'):
        tickv = game.ticks
    if kpr == ord(' '):
        r = a.drop()
    else:
        r = a.tick(tickv)
    return r
        
def kpract_r(a):
    "act as robot"
    tickv = 1
    r = 1
    kpr = ord(whrandom.choice(('u', 'i', 'o')))
    if whrandom.random()>0.3:
        kpr = -1
    if kpr == ord('u'):
        a.left()
    elif kpr == ord('o'):
        a.right()
    elif kpr == ord('i'):
        a.rotate()
    elif kpr == ord('m'):
        tickv = game.ticks
    if kpr == ord(' '):
        r = a.drop()
    else:
        r = a.tick(tickv)
    return r




def play():

    global net
    if globalvar.wait:
        while 1:
            net.sendmsg("R", 0) # request to run
            time.sleep(1)
            m = net.receivemsg()
            if m=="R\n" or m=="U\n": # other side is alive
                break
        while 1:
            net.sendmsg("U", 0)
            time.sleep(1)
            m = net.receivemsg()
            if m<>None and m<>"R\n": # other side is already playing
                break
    
        for i in range(10): 
            net.sendmsg("U", 0)
            time.sleep(0.2)
        
    global game
    display.drawsep("=")

    game.hispiece = HisPiece()
    game.hismatrix = copy.deepcopy(game.matrix)

    while 1:
        game.piece = Piece(pieces.getpiece())
        display.shownext()

        while 1:
            m = net.receivemsg()
            r = actonmsg(m)
            if r==3 or r==4:
                break
            r = 1
            time.sleep(game.sleeptime)
            r = globalvar.kpract(game.piece)
            if r == 2 or r == 3 or r == 4: # bottom or end of game
                break
        if r == 3 or r == 4: # end of game
            break
    display.redisplay(game.xsize, game.hisbottomline)
    display.refresh()
    mybeep()
    sckey = 'Winners'
    
    if r == 3:
        for i in range(5*globalvar.wait+1): # give the net 5 seconds
            net.sendmsg("L", 1)
            time.sleep(1)
            m = net.receivemsg()
            if m=="E\n" or m=="V\n": # other side signals end of game or victory
                break
        s = "You lost! Your score is %i." % game.score
        sckey = 'Loosers'
    elif r == 4:    
        for i in range(5*globalvar.wait+1): # give the net 5 seconds
            net.sendmsg("V", 1)
            time.sleep(1)
            m = net.receivemsg()
            if m=="E\n" or m=="L\n": # other side signals end of game or defeat
                break
        s = "You won! Your score is %i." % game.score
    else:
        for i in range(5*globalvar.wait+1): # give the net 5 seconds
            net.sendmsg("E", 1)
            time.sleep(1)
            m = net.receivemsg()
            if m=="E\n" or m=="L\n" or m=="V\n":
                break
        s = "What happened?"
    time.sleep(1)

    del(net) # so that we do not block ports
    
    finscore(1, sckey)

    display.showinmiddle(s)
    mybeep()
    time.sleep(3)
    
    finalwait()

    
def finscore(wr, sckey=None): # show final score
    
    loadscore()
    username = string.split(pwd.getpwuid(os.getuid())[4], ",")[0]
    opt = `globalvar.xsize*100+globalvar.ysize`+globalvar.pieces+`globalvar.pushing` # simple hash
    opt = hash(opt)
    if wr:
        try:
            globalvar.score[opt][sckey].append( (game.score, username, 1) )
        except KeyError:
            globalvar.score[opt] = {'Loosers':[], 'Winners':[]}
            globalvar.score[opt][sckey].append( (game.score, username, 1) )
        globalvar.score[opt][sckey].sort()
        globalvar.score[opt][sckey].reverse()
        if len(globalvar.score[opt][sckey]) > 30:
            globalvar.score[opt][sckey] = globalvar.score[opt][sckey][:30]

    display.showhighscores(opt)
    
    if wr:
        globalvar.score[opt][sckey] = map(lambda x: x[:2]+(0,), globalvar.score[opt][sckey])
        savescore()


def finalwait():
    s ="       >>> Press ENTER <<<       "
    display.showinmiddle(s)
    mybeep()
    while 1:
        k = display.stdscr.getch()
        if k == 13:
            break
    sys.exit(0)



def help():
    print """

pytris [-options] hostname otherport myport
or
pytris [-options] -1|2 hostname

-a n, --ascii-chars=n
    if n=0, do not use ascii characters to draw pieces
-c n, --colour=n
    if n=0, do not use colour
-r n, --inverse=n
    if n=1, use reverse colour
-x n, --vsize=n
    set vertical size to n
-y n, --hsize=n
    set horizontal size to n
--hzoom=n
    set horizontal zoom
--vzoom=n
    set vertical zoom
-p name, --pieces=name
    select type of falling pieces, name is one of:
    tritris
    tetris
    extended
    pentris
    crazy
-n n, --nlevel=n
    select network level, 1<=n<=3
    the bigger level, the more information is transferred
-h, --help
-v, --version
-w n, --wait=n
    if n=0, do not wait for your oponent to start game
--robot=n
    if n=1, act as a (completely dumb) robot
--pushing=n
    push screen by n lines down when you complete a line.
    default n=1
--beep=n
    if n=0, do not beep at the end of game
    default: do beep
--flip=n
    if n=0, do not allow flipping (default)
    if n=1, x flips horizontally
    if n=2, x flips vertically
--shadow=n
    if n=0, do not show shadow
    if n=1, show black shadow (default)
    if n=2, show nice colourful shadow
--next=n
    show n next pieces
--scores
    only display high score table for given command line parameters
-1
    set myport 5634, otherport 5635
-2
    set myport 5635, otherport 5634
""" 
    sys.exit(0)


def version():
    print "0.96"
    sys.exit(0)

globalvar = Globalvar()

try:
    optlist, args = getopt.getopt(sys.argv[1:], "a:c:r:x:y:n:p:w:vh12",
                        ["ascii-chars=", "colour=", "inverse=",
                         "hsize=", "vsize=", "pieces=", "nlevel=",
                         "wait=", "robot=", "hzoom=", "vzoom=", "pushing=",
                         "happyholik=", "beep=", "flip=", "next=", "shadow=",
                         "scores",
                         "version", "help"])
except:
    help()


if len(args) not in [0,1,3]:
    help()

if len(args) == 3:
    globalvar.otherport = int(args[2])
    globalvar.myport = int(args[1])

net = 0

if len(args) == 0: # single player
    globalvar.nlevel = 0
    globalvar.wait = 0
    globalvar.pushing = 0
    net = DummyNet()
else:
    globalvar.otherhost = args[0]

onlyshowscores = 0

for i in optlist:
    if i[0] in ["--ascii-chars", "-a"]:
        globalvar.ascii = int(i[1])
    elif i[0] in ["--colour", "-c"]:
        globalvar.colours = int(i[1])
    elif i[0] in ["--inverse", "-r"]:
        globalvar.inverse = int(i[1])
    elif i[0] in ["--vsize", "-x"]:
        globalvar.xsize = int(i[1])
    elif i[0] in ["--hsize", "-y"]:
        globalvar.ysize = int(i[1])
    elif i[0] in ["--pieces", "-p"]:
        globalvar.pieces = i[1]
    elif i[0] in ["--nlevel", "-n"]:
        globalvar.nlevel = int(i[1])
    elif i[0] in ["--wait", "-w"]:
        globalvar.wait = int(i[1])
    elif i[0] == "--hzoom":
        globalvar.zoom = (globalvar.zoom[0], int(i[1]))
    elif i[0] == "--vzoom":
        globalvar.zoom = (int(i[1]), globalvar.zoom[1])
    elif i[0] == "--pushing":
        globalvar.pushing = int(i[1])
    elif i[0] == "--happyholik":
        globalvar.happyholik = int(i[1])
    elif i[0] == "--beep":
        globalvar.beep = int(i[1])
    elif i[0] == "--flip":
        globalvar.flip = int(i[1])
    elif i[0] == "--shadow":
        globalvar.shadow = int(i[1])
    elif i[0] == "--next":
        globalvar.next = int(i[1])
    elif i[0] == "--scores":
        onlyshowscores = 1
    elif i[0] == "--robot":
        if i[1] == "1":
            globalvar.kpract = kpract_r
        else:
            globalvar.kpract = kpract
    elif i[0] == "-1":
        globalvar.otherport = 5635
        globalvar.myport = 5634
    elif i[0] == "-2":
        globalvar.otherport = 5634
        globalvar.myport = 5635
    elif i[0] in ["--version", "-v"]:
        version()
    elif i[0] in ["--help", "-h"]:
        help()



def disp(stdscr):
    global display, net, game, pieces
    display = Display(stdscr)
    if not onlyshowscores:
        if not net:  # i.e. if not single player
            net = Net()
        game = Game()
        pieces = Pieces(globalvar.pieces)
        play()
    else:
        finscore(0)
        finalwait()

wrapper(disp)

