#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2020-03-31 01:05
# @Author  : cxw
# @File    : terminal
# @Software: PyCharm

import math
from . import terminal_router
from app.model.history import History
from app.model.summary import Summary
from app.comm.utils import *
from flask import jsonify
from app.validators.terminal import ListForm,ChartForm,Real_time

@terminal_router.route('/meslist',methods=["POST"])
def meslist():
    form = ListForm().check_param()
    query =History.get_mes(form)
    page = int(form.page_index.data)
    page_size = form.page_size.data
    result= [x.to_dict() for x in query.items]
    length_query = query.total
    total_count = math.ceil(length_query / page_size)
    pagination = {
        'pagination': {
            'total_count': length_query,
            'page_count': total_count,
            'count': page_size,
            "page_index": int(page),
        },
    }
    if result:
        return jsonify(true_return(msg="请求成功",data={"pagination":pagination["pagination"],"data":result}))
    else:
        return jsonify(false_return(msg="请求失败"))

@terminal_router.route('today_detection',methods=["POST"])
def today_detection():
    query = Summary.get_mes()
    return jsonify(true_return(msg="请求成功",data=query))

@terminal_router.route('/chart',methods=["POST"])
def chart():
    form =ChartForm().check_param()
    # if form.date.data:
    #     start =form.date.data[0][:10]
    #     end =form.date.data[1][:10]
    #     date_list = []
    #     date_list = func_days(start, end, date_list)[::-1]
    #     result, result1, result2 = History.get_chat(date_list)
    # else:
    today = datetime.date.today()
    date_list=[x for x in range(24)]
    result,result1,result2,result3,sy = History.get_chat1(today.__str__())
    if result:
        return jsonify(true_return(msg="请求成功",data={"data":result,"date_list":date_list,"data1":result1,"data2":result2,"data3":result3,"sy":sy}))
    else:
        return jsonify(false_return(msg="请求失败"))

@terminal_router.route('/chart2',methods=["POST"])
def chart2():
    form =ChartForm().check_param()
    today = datetime.date.today()
    target = (today - datetime.timedelta(days=8))
    target_res = target.strftime("%Y-%m-%d")
    date_list = func(today,[])[::-1]
    result= History.get_chat2(target_res,today,date_list)
    if result:
        return jsonify(true_return(msg="请求成功",data={"data":result,"date_list":date_list}))
    else:
        return jsonify(false_return(msg="请求失败"))


from bigPrimesGenerator import BigPrimesGenerator
from BBS import BBS
from tests import *

if __name__ == '__main__':
    p, q = BigPrimesGenerator(2, 20).primes_list
    print('Primes for BBS algorithm:', p, q)

    bbs = BBS(p, q)
    bits = bbs.generate_bits(20000)
    bbs.visualize(bits)

    # tests
    frequency_test(
        bits,
        lower_threshold=9725, upper_threshold=10275
    )

    series_test(
        bits, '0',
        [
            [2315, 2685],
            [1114, 1386],
            [ 527,  723],
            [ 240,  384],
            [ 103,  209],
            [ 103,  209]
        ], long_series_threshold=26
    )
    series_test(
        bits, '1',
        [
            [2315, 2685],
            [1114, 1386],
            [527, 723],
            [240, 384],
            [103, 209],
            [103, 209]
        ], long_series_threshold=26
    )

    pokerTest(bits)


from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import sys
from PyQt5.QtWidgets import *
from PyQt5 import*
from PyQt5 import uic
import numpy as np
from sympy import*
import matplotlib.pyplot as plt
from sympy.core.numbers import Infinity

form_class6 = uic.loadUiType("FinR.ui")[0]

class FinR(QMainWindow, form_class6):

    val4else = ['T(x)','q','q4C','x','L','m','M','t']

    def __init__(self):

        self.graphwindow = Graphwindow()
        self.opengl = Opengl()

        super().__init__()
        self.setupUi(self)

        self.Radiobutton = [self.radioButton_1,self.radioButton_2,self.radioButton_3,
            self.radioButton_4]

        def BC():
            for a,b in zip(self.Radiobutton,range(4)):
                if a.isChecked():
                    self.Radiosel[0] = b
                    print(b)

        self.Radiosel = np.ones(1,dtype= np.int0)*5

        for a in self.Radiobutton:
            a.toggled.connect(BC)

        self.pushButton_1.clicked.connect(self.Calfin)
        self.pushButton_2.clicked.connect(self.show_graph)
        self.pushButton_3.clicked.connect(self.show_opengl)

    def Calfin(self):

        Bc = self.Radiosel

        #공통 변수 설정..
        try:
            w = float(self.lineEdit_w.text())

        except ValueError:
            w = 1

        x = symbols('x')
        L = symbols('L')

        k = float(self.lineEdit_k.text())
        h = float(self.lineEdit_h.text())
        t = float(self.lineEdit_t.text())
        self.val4else[7] = t

        T_b = float(self.lineEdit_tb.text())
        T_a =float(self.lineEdit_ta.text())

        theta_b = T_b - T_a
        P = 2*(w+t)
        A = w*t

    #입력받은 값을 이용해서 m,M,theha_b 를 얻음

        M = self.fun_M(h,P,k,A,theta_b)
        m = self.fun_m(h,P,k,A)
        theta  = self.Case_A_temperature(x,m,L,k,h)

        if Bc == 0:             #BC1

            L = float(self.lineEdit_1_L.text())
            theta  = self.Case_A_temperature(x,m,L,k,h)

            T = theta*(theta_b) + T_a
            q = self.Case_A_heatrate(M,m,L,k,h)

        #따로 저장해 놓을것 --> 다른곳에서 쓸 수 있음
            self.val4else[0] = T
            self.val4else[1] = q
            self.val4else[4] = L

            try:
                x1 = float(self.lineEdit_1_x.text())

                self.textBrowser_1.append(" T({0}) = {1} ".format(str(x1),str(T.subs(x,x1))))
                self.textBrowser_1.append(" T(x) = {0} ".format(str(N(T,3))))
                self.textBrowser_2.append(" q = {0}".format(str(q)))

            except ValueError:

                self.textBrowser_1.append(" T({0}) ={1} ".format((str(x),str(T))))
                self.textBrowser_1.append(" T(x) ={0} ".format(str(N(T,3))))
                self.textBrowser_2.append(" q = {0} ".format(str(q)))

        elif Bc == 1:               #BC2

            L = float(self.lineEdit_2_L.text())
            theta  = self.Case_B_temperature(x,m,L)

            T = theta*(theta_b) + T_a
            q = self.Case_B_heatrate(M,m,L)

        #따로 저장해 놓을것 --> 다른곳에서 쓸 수 있음
            self.val4else[0] = T
            self.val4else[1] = q
            self.val4else[4] = L

            print(self.val4else[0])

            try:
                x1 = float(self.lineEdit_2_x.text())

                self.textBrowser_1.append(" T({0}) = {1} ".format(str(x1), str(T.subs(x,x1))))
                self.textBrowser_1.append(" T(x) = {0} ".format(str(T)))
                self.textBrowser_2.append(" q = {0}".format(str(q)))

            except ValueError:

                self.textBrowser_1.append(" T({0}) ={1} ".format(str(x),str(T.subs(x,x))))
                self.textBrowser_2.append(" q = {0} ".format(str(q)))

        elif Bc == 2:               #BC3

            L   = float(self.lineEdit_3_L.text())
            T_L = float(self.lineEdit_3_T.text())

            theta_L = T_L - T_a
            theta_b = T_b - T_a

            theta  = self.Case_C_temperature(x,m,L,theta_L,theta_b)

            T = theta*(theta_b) + T_a

            q = self.Case_C_heatrate(M,m,L,theta_L,theta_b)

        #따로 저장해 놓을것 --> 다른곳에서 쓸 수 있음

            self.val4else[0] = T
            self.val4else[1] = q[0]
            self.val4else[2] = q[1]
            self.val4else[4] = L

            try:
                x1 = float(self.lineEdit_3_x.text())

                self.textBrowser_1.append(" T({0}) ={1} ".format(str(x1),str(T.subs(x,x1))))
                self.textBrowser_1.append(" T(x) ={0} ".format(str(T)))

                self.textBrowser_2.append(" q0 = {0}".format(str(q[0])))
                self.textBrowser_2.append(" qL = {0}".format(str(q[1])))

            except ValueError:

                self.textBrowser_1.append(" T({0}) ={1} ".format(str(x),str(T.subs(x,x))))
                self.textBrowser_1.append(" T(x) ={0} ".format(str(T)))

                self.textBrowser_2.append(" q0 = {0}".format(str(q[0])))
                self.textBrowser_2.append(" qL = {0}".format(str(q[1])))

        elif Bc == 3:               #BC3

            theta  = self.Case_D_temperature(x,m)

            T = theta*(theta_b) + T_a
            q = self.Case_D_heatrate(M)

            L = 10

        #따로 저장해 놓을것 --> 다른곳에서 쓸 수 있음
            self.val4else[0] = T
            self.val4else[1] = q
            self.val4else[4] = L

            try:
                x1 = float(self.lineEdit_4_x.text())

                self.textBrowser_1.append(" T({0}) ={1} ".format(str(x1),str(T.subs(x,x1))))
                self.textBrowser_1.append(" T(x) ={0} ".format(str(T)))

                self.textBrowser_2.append(" q = {0}".format(str(q)))

            except ValueError:

                self.textBrowser_1.append(" T({0}) ={1} ".format(str(x),str(T.subs(x,x))))
                self.textBrowser_1.append(" T(x) ={0} ".format(str(T)))

                self.textBrowser_2.append(" q = {0} ".format(str(q)))

        else:
            pass

    def fun_m(self,h,P,k,A):
        return sqrt((h*P)/(k*A))

    def fun_M(self,h,P,k,A,theta_b):

        return sqrt(h*P*k*A)*theta_b

    def Case_A_temperature(self,x, m, L, k, h):
        # convection
        d1 = N(cosh(m*(L-x)) + (h/(m*k))*sinh(m*(L-x)),2)
        d2 = N(cosh(m*L) + (h/(m*k))*sinh(m*L),2)

        return N(d1/d2,2)

    def Case_A_heatrate(self,M, m, L, k, h):
        # convection
        d1 = sinh(m*L) + (h/(m*k))*cosh(m*L)
        d2 = cosh(m*L) + (h/(m*k))*sinh(m*L)
        return M*d1/d2

    def Case_B_temperature(self,x, m, L):
        # adiabatic
        return cosh(m*(L-x))/cosh(m*L)

    def Case_B_heatrate(self,M, m, L):
        # adiabatic
        return M*tanh(m*L)

    def Case_C_temperature(self,x, m, L, theta_L, theta_b):
        # prescribed temperature
        d1 = (theta_L/theta_b)*sinh(m*x) + sinh(m*(L-x))
        return d1/sinh(m*L)

    def Case_C_heatrate(self,M, m, L, theta_L, theta_b):
        # prescribed temperature
        q0 = M/sinh(m*L) * (cosh(m*L) - (theta_L/theta_b))
        qL = M/sinh(m*L) * (1 - cosh(m*L)*(theta_L/theta_b))
        return [q0, qL]

    def Case_D_temperature(self,x,m):
        # infinite fin
        return exp(-m*x)

    def Case_D_heatrate(self,M):
        # infinite fin
        return M

    def show_graph(self):
        self.graphwindow.show()

    def show_opengl(self):
        self.opengl.show()

class Graphwindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.main_widget = QWidget()                            #Qwiget 을 만듬
        self.setCentralWidget(self.main_widget)                 #QMainWindow에 이걸해야지 뭐든지 추가할 수있는거임..

        button = QPushButton("Show Graph")                      #버튼을 생성함
        self.canvas = FigureCanvas(Figure(figsize=(4, 3)))      #canvas도 생성함
        vbox = QVBoxLayout(self.main_widget)                    #layout도 생성함
        self.ax = self.canvas.figure.subplots()

        vbox.addWidget(self.canvas)                             #layout에 canvas를 추가
        vbox.addWidget(button)                                  #이렇게 추가해야지 실제로 적용되는것.
        self.addToolBar(NavigationToolbar(self.canvas, self))   #toolbor 추가

        self.setWindowTitle('Graph')                            #이름,크기지정
        self.setGeometry(300, 100, 600, 400)
        button.clicked.connect(self.update_chart)               #버튼 기능

    def update_chart(self):                                     #그래프 추가하기
        x = symbols("x")
        f = lambdify(x,FinR.val4else[0],"numpy")

        x1 = 0
        x2 = FinR.val4else[4]
        ex = (x2-x1)/20

        x = np.linspace(x1,x2,100)
                                                                #grid 추가하는 방법 찾아보기.....

        self.ax.grid()
        self.ax.set_xlim([x1-ex, x2+ex])
        self.plot = self.ax.plot(x,f(x),'r')                    #이렇게 새로운 값을 대입할 수 있는것

        self.canvas.draw()                                      #show 말고 draw 그림

class Opengl(QOpenGLWidget):                                    #그림그리기.....

    x = symbols('x')

    def  __init__(self):
        super().__init__()

    def initializeGL(self):
        glPolygonMode(GL_FRONT, GL_FILL)
        glPolygonMode(GL_BACK, GL_FILL)

        glShadeModel(GL_SMOOTH)
        glEnable(GL_NORMALIZE)

        glClearDepth(1.0)
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)

        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glEnable(GL_BLEND)

        glEnable(GL_DEPTH_TEST)
        glEnable(GL_TEXTURE_2D)

    def resizeGL(self, width, height):
        glGetError()

        aspect = width if (height == 0) else width / height

        glViewport(0, 0, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45, aspect, 0.1, 1000.0)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

    def paintGL(self):

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()

        glClearColor(0.2, 0.2, 0 , 0)                                #바탕화면 색깔
        glPushMatrix()
        glTranslatef(0.0, 0.0, -3.0)                                 #시점조정하기...
        self.Draw()
        glPopMatrix()
        glFlush()

    def Draw(self):

        def Vertices():                                              #좌표......

            x = np.linspace(-1.5,1.5,200)
            ver= []

            t = FinR.val4else[7]
            L = FinR.val4else[4]

            for a in x:
                ver1 = [a,-0.2]
                ver2 = [a, 0.2]
                ver.append(ver1)
                ver.append(ver2)

            return ver

        def Color():                                                   #색배합 조정하기

            expr = lambdify(self.x,FinR.val4else[0],'numpy')
            x = np.linspace(0,FinR.val4else[4],200)
            T = np.array(expr(x))
            T_avg = np.average(T)                   #평균값

            dT = T-T_avg                            # dT= 온도 - 평균
            dT_max = np.max(dT)
            dT_min = np.min(dT)

            color= []

            for T in dT:
                if T > 0:
                    if    dT_max >= T >=  0.93*dT_max:
                        color1 = [253/253,0,0]
                        color2 = [253/253,0,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.93*dT_max> T >= 0.86*dT_max:
                        color1 = [253/253,34/253,0]
                        color2 = [253/253,34/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.86*dT_max> T >= 0.79*dT_max:
                        color1 = [253/253,68/253,0]
                        color2 = [253/253,68/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.79*dT_max> T >= 0.72*dT_max:
                        color1 = [253/253,102/253,0]
                        color2 = [253/253,102/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.72*dT_max> T >= 0.65*dT_max:
                        color1 = [253/253,136/253,0]
                        color2 = [253/253,136/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.65*dT_max> T >= 0.58*dT_max:
                        color1 = [253/253,170/253,0]
                        color2 = [253/253,170/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.58*dT_max> T >= 0.51*dT_max:
                        color1 = [253/253,204/253,0]
                        color2 = [253/253,204/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.51*dT_max> T >= 0.44*dT_max:
                        color1 = [253/253,238/253,0]
                        color2 = [253/253,238/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.44*dT_max> T >= 0.37*dT_max:
                        color1 = [233/253,253/253,0]
                        color2 = [233/253,253/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.37*dT_max> T >= 0.30*dT_max:
                        color1 = [199/253,253/253,0]
                        color2 = [199/253,253/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.30*dT_max> T >= 0.23*dT_max:
                        color1 = [165/253,253/253,0]
                        color2 = [165/253,253/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.23*dT_max> T >= 0.16*dT_max:
                        color1 = [131/253,253/253,0]
                        color2 = [131/253,253/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.16*dT_max> T >= 0.09*dT_max:
                        color1 = [97/253,253/253,0]
                        color2 = [97/253,253/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.09*dT_max> T >= 0.02*dT_max:
                        color1 = [63/253,253/253,0]
                        color2 = [63/253,253/253,0]
                        color.append(color1)
                        color.append(color2)

                    elif 0.02*dT_max> T >= 0.00*dT_max:
                        color1 = [29/253,253/253,0]
                        color2 = [29/253,253/253,0]
                        color.append(color1)
                        color.append(color2)

                else:
                    if dT_min <= T < 0.93*dT_min:               #가장 차가운
                        color1 = [0,25/253,253/253]
                        color2 = [0,25/253,253/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.93*dT_min <= T < 0.86*dT_min:
                        color1 = [0,59/253,253/253]
                        color2 = [0,59/253,253/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.86*dT_min <= T < 0.79*dT_min:
                        color1 = [0,93/253,253/253]
                        color2 = [0,93/253,253/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.79*dT_min <= T < 0.72*dT_min:
                        color1 = [0,127/253,253/253]
                        color2 = [0,127/253,253/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.72*dT_min <= T < 0.65*dT_min:
                        color1 = [0,161/253,253/253]
                        color2 = [0,161/253,253/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.65*dT_min <= T < 0.58*dT_min:
                        color1 = [0,195/253,253/253]
                        color2 = [0,195/253,253/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.58*dT_min <= T < 0.51*dT_min:
                        color1 = [0,229/253,253/253]
                        color2 = [0,229/253,253/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.51*dT_min <= T < 0.44*dT_min:
                        color1 = [0,253/253,243/253]
                        color2 = [0,253/253,243/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.44*dT_min <= T < 0.37*dT_min:
                        color1 = [0,253/253,209/253]
                        color2 = [0,253/253,209/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.37*dT_min <= T < 0.30*dT_min:
                        color1 = [0,253/253,175/253]
                        color2 = [0,253/253,175/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.30*dT_min <= T < 0.23*dT_min:
                        color1 = [0,253/253,141/253]
                        color2 = [0,253/253,141/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.23*dT_min <= T < 0.16*dT_min:
                        color1 = [0,253/253,107/253]
                        color2 = [0,253/253,107/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.16*dT_min <= T < 0.09*dT_min:
                        color1 = [0,253/253,73/253]
                        color2 = [0,253/253,73/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.09*dT_min <= T < 0.02*dT_min:
                        color1 = [0,253/253,39/253]
                        color2 = [0,253/253,39/253]
                        color.append(color1)
                        color.append(color2)

                    elif 0.02*dT_min <= T < 0.00*dT_min:
                        color1 = [0,253/253,5/253]
                        color2 = [0,253/253,5/253]
                        color.append(color1)
                        color.append(color2)

            return color

        glBegin(GL_TRIANGLE_STRIP)                              #(GL_TRIANGLE_STRIP으로 그릴것

        for a,b in zip(Vertices(),Color()):

            glColor3fv(b)                                       #색깔이 먼저 언급되어야한다.
            glVertex2fv(a)                                      #그다음이 좌표임

        glEnd()

        glBegin(GL_TRIANGLE_STRIP)                              #핀그림

        glColor3fv((253/253,0,0))
        glVertex2fv((-2 ,- 2))
        glColor3fv((253/253,0,0))
        glVertex2fv((-1.5,-2))
        glColor3fv((253/253,0,0))
        glVertex2fv((-2 ,  2))
        glColor3fv((253/253,0,0))
        glVertex2fv((-1.5, 2))

        glEnd()

def Fin_show():
    app = QApplication(sys.argv)
    myWindow = FinR()
    myWindow.show()
    sys.exit(app.exec_())


# encoding: utf-8

'''
今日练习：selenium 爬虫 + excel表格操作（读写、修改）

demo：搜集web自动化原创微博(前5页)的 发送人，标题，发布时间，来源，收藏数，转发数，评论数，点赞数写入excel表格

'''

import time, xlrd, xlwt
from selenium import webdriver
from xlutils.copy import copy

driver = webdriver.Chrome()

driver.get('https://s.weibo.com/')

def append_data(init_row, sheet, data_list):
    count = init_row
    for ele in data_list:
        count += 1
        userName = ele.find_element_by_css_selector('a.name').text
        title = ele.find_element_by_css_selector('p.txt').text
        time = ele.find_element_by_css_selector('p.from > a:nth-of-type(1)').text
        source = ele.find_element_by_css_selector('p.from > a:nth-of-type(2)').text
        collect = ele.find_element_by_css_selector('div.card-act ul li:nth-of-type(1)').text.split('收藏')[1] or 0
        share = ele.find_element_by_css_selector('div.card-act ul li:nth-of-type(2)').text.split('转发')[1] or 0
        comment = ele.find_element_by_css_selector('div.card-act ul li:nth-of-type(3)').text.split('评论')[1] or 0
        like = ele.find_element_by_css_selector('div.card-act ul li:nth-of-type(4)').text or 0

        sheet.write(count, 0, title)
        sheet.write(count, 1, userName)
        sheet.write(count, 2, time)
        sheet.write(count, 3, source)
        sheet.write(count, 4, collect)
        sheet.write(count, 5, share)
        sheet.write(count, 6, comment)
        sheet.write(count, 7, like)

def create_excel():
    driver.find_element_by_xpath('//*[@id="pl_homepage_search"]/div/div[2]/div/input').send_keys('web自动化')
    driver.find_element_by_xpath('//*[@id="pl_homepage_search"]/div/div[2]/button').click()
    ele_search_high = driver.find_element_by_xpath('//*[@id="pl_feedtop_top"]/div[3]/a')
    if (ele_search_high.is_displayed()):
        ele_search_high.click()
        driver.find_element_by_xpath('//*[@id="radio03"]').click()
        driver.find_element_by_xpath('/html/body/div[7]/div[2]/div/div[2]/a[1]').click()

        # 建表
        excel = xlwt.Workbook()
        sheet1 = excel.add_sheet('web自动化原创微博基本信息收集')
        sheet1.write(0, 0, '标题')
        sheet1.write(0, 1, '发送人')
        sheet1.write(0, 2, '发布时间')
        sheet1.write(0, 3, '来源')
        sheet1.write(0, 4, '收藏数')
        sheet1.write(0, 5, '转发数')
        sheet1.write(0, 6, '评论数')
        sheet1.write(0, 7, '点赞数')

        # 写入微博第1页列表数据
        list = driver.find_elements_by_css_selector("#pl_feedlist_index div[action-type='feed_list_item']")
        append_data(0, sheet1, list)

        # 保存第1页数据
        excel.save('./weboList.xls')

        # 切换页并将数据追加到已经建好的数据表格中
        eles_page_list = driver.find_elements_by_css_selector('#pl_feedlist_index > div.m-page > div > span > ul > li')
        for i in range(len(eles_page_list)):
            if (i < 4):
                switch_page(i + 2)

def switch_page(pageNum):
    driver.find_element_by_xpath('//*[@id="pl_feedlist_index"]/div[3]/div/span/a').click()

    ele_page = '#pl_feedlist_index > div.m-page > div > span > ul > li:nth-of-type({})'.format(pageNum)

    # 切换到指定页
    driver.find_element_by_css_selector(ele_page).click()

    # 复制已经建好的表将新数据写入再重新保存
    excel = xlrd.open_workbook('./weboList.xls')
    sheet = excel.sheet_by_name('web自动化原创微博基本信息收集')
    rows_old = sheet.nrows
    excel_new = copy(excel)
    sheet_new = excel_new.get_sheet(0)
    list = driver.find_elements_by_css_selector("#pl_feedlist_index div[action-type='feed_list_item']")
    append_data(rows_old - 1, sheet_new, list)
    excel_new.save('./weboList.xls')
    if (pageNum == 5):
        driver.quit()

create_excel()


# https://en.wikipedia.org/wiki/Leap_year

while True:

    year = input("Please enter a year: ")
    year = int(year)

# Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100,
# but these centurial years are leap years if they are exactly divisible by 400.
    if year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
        print("It's a leap year!")
    else:
        print("This is not a leap year year")


import numpy as np
import pandas as pd
from shapely import geometry
import geopandas as gpd

#### MAKE GRID

x = np.arange(-130,-65, 0.125)
y = np.arange(24, 50, 0.125)

ssize = 0.125

xy_ll = np.vstack(np.dstack(np.meshgrid(x, y)))
xy_ur = xy_ll + ssize
xy_lr = np.column_stack([xy_ll[:,0] + ssize, xy_ll[:,1]])
xy_ul = np.column_stack([xy_ll[:,0], xy_ll[:,1] + ssize])

cs = np.column_stack([xy_ur, xy_ul, xy_ll, xy_lr, xy_ur])
cs_shape = cs.shape
cs = cs.reshape(cs_shape[0], cs_shape[1]/2, 2)

#CHEESY WAY
gs = gpd.GeoSeries(pd.Series(cs.tolist()).apply(lambda x: geometry.Polygon(x)))

#MORE ELEGANT, BUT SLOWER
#gpd.GeoSeries(map(geometry.Polygon, cs))

#ABOUT AS SLOW AS MAP
#[geometry.Polygon(i) for i in cs]


import numpy as np
import torch.nn as nn
import torch
import matplotlib.pyplot as plt
import h5py

class find_learning_rate():

    def __init__(self, min_lr, max_lr, num_points, epochs, model, optimizer, loss_fn, data, dim_reduction=None,
                 device='cpu', space='linear'):

        self.min_lr = min_lr
        self.max_lr = max_lr
        self.epochs = epochs
        self.num_points = num_points
        self.final_loss = []
        self.model = model
        self.optimizer = optimizer
        self.loss_fn = loss_fn
        self.data = data
        self.dim_reduction = dim_reduction
        self.device = device
        self.lr_space = []
        self.space = space

    def get_lr_space(self):

        if self.space == 'linear':
            lr_space = np.linspace(self.min_lr, self.max_lr, self.num_points)
        elif self.space == 'log':
            lr_space = np.logspace(np.log10(self.min_lr), np.log10(self.max_lr), self.num_points)

        self.lr_space = lr_space
        return lr_space

    def train_lr(self):

        for lr in self.get_lr_space():

            self.optimizer.defaults['lr'] = lr

            for epoch in np.arange(self.epochs):

                tot_loss = 0

                for _, (x, y) in enumerate(self.data):

                    if self.dim_reduction is not None:

                        with torch.no_grad():
                            x = x.to(self.device)
                            x = self.dim_reduction.get_representation(x)

                    self.optimizer.zero_grad()

                    pred_y = self.model(x)
                    loss = self.loss_fn(pred_y, y.to(self.device))
                    loss.backward()
                    self.optimizer.step()

                    tot_loss += loss.to('cpu').detach().numpy()

                print('Completed epoch ' + str(epoch) + ' for lr ' + str(lr) + ' loss is ' + str(tot_loss.item()))

            self.final_loss.append(tot_loss.item())

    def find_lr(self):

        self.train_lr()

        plt.figure()
        plt.semilogx(self.lr_space, self.final_loss)
        plt.show()
        plt.savefig('LearningRate.jpg')

        return self.lr_space, self.final_loss


import random

def main():
    # Check if there questions have been answered consecutively
    counter = 0
    while True:
        if counter != 3:
            numOne = random.randint(0, 10)
            numTwo = random.randint(0, 10)
            print("What is the addition of {} and {}".format(numOne, numTwo))
            answer = input("Your answer is ")
            answer = int(answer)
            if answer == (numOne + numTwo):
                counter = counter + 1
                print("Correct answer! You have answered {} questions correctly".format(counter))
            else:
                counter = 0
                print("Wrong answer, the game is reset")
        else:
            print("The game ends here since you have answered 3 questions correctly")
            break
if __name__ == "__main__":
    main()


import json
import es
import zlib
import base64
import datetime

def lambda_handler(event, context):
    payloads = []
    for record in event['Records']:
        sns_message = json.loads(record["body"])
        try:
            decoded = json.loads(zlib.decompress(base64.b64decode(sns_message["Message"]), 16 + zlib.MAX_WBITS))
        except:
            decoded = json.loads(sns_message["Message"])
        if type(decoded) == dict:
            incoming_payloads = [decoded]
        else:
            incoming_payloads = decoded
        year, week = datetime.datetime.now().isocalendar()[:2]
        index = f"{year}-{week}"
        payloads += incoming_payloads

    body=""
    for payload in payloads:
        body += "{\"index\":{}}\n" + json.dumps(payload) + "\n"
    body += "\n"

    result = es.request(body, f"telm-{index}/_bulk", "POST")
    if 'errors' in result and result['errors'] == True:
        error_types = [x['index']['error']['type'] for x in result['items'] if 'error' in x['index']] # get all the error types
        print(event)
        print(result)
        error_types = [a for a in error_types if a != 'mapper_parsing_exception'] # filter out mapper failures since they will never succeed
        if error_types:
            raise RuntimeError


from django.forms import ModelForm
from .models import MenuItem, FoodCategory,Order
from django.contrib.auth.forms import AuthenticationForm
from django.forms import CheckboxSelectMultiple

class CustomAuthForm(AuthenticationForm):
    def __init__(self,*a,**ka):
        super(CustomAuthForm,self).__init__(*a,**ka)
        for i in self.fields:
            self.fields[i].widget.attrs.update({'class':'form-control'})

class BootstrapFormMixin(object):
    """ this mixin add input's class 'form-control'  from bootstrap """
    def __init__(self,*a,**ka):
        super(BootstrapFormMixin,self).__init__(*a,**ka)
        for i in self.fields:
            self.fields[i].widget.attrs.update({'class':'form-control'})

class MenuItemForm(ModelForm):
    def __init__(self,*a,**ka):
        super(MenuItemForm,self).__init__(*a,**ka)
        for i in self.fields:
            self.fields[i].widget.attrs.update({'class':'form-control'})
        #self.fields['category'].widget.attrs = {'class':'foo'}
        #self.fields['category'].label_classes= ('shit',)

    class Meta:
        model = MenuItem
        fields =  ['name','description','category','price','image']
        widgets = {
        #        'category':CheckboxSelectMultiple()
                }

class OrderForm(ModelForm):
    def __init__(self,*a,**ka):
        super(OrderForm,self).__init__(*a,**ka)
        for i in self.fields:
            self.fields[i].widget.attrs.update({'class':'form-control'})
    class Meta:
        model = Order
        fields = ['item','quantity','table','parcle']

class FoodCategoryForm(ModelForm):

    def __init__(self,*a,**ka):
        super(FoodCategoryForm,self).__init__(*a,**ka)
        for i in self.fields:
            self.fields[i].widget.attrs.update({'class':'form-control'})

    class Meta:
        model = FoodCategory
        fields = ['name',]

from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django import forms
class AccountForm(forms.Form):
    username = forms.CharField(max_length=20)
    password1 = forms.CharField(max_length=20,widget=forms.widgets.PasswordInput,label="Password")
    password2 = forms.CharField(max_length=20,widget=forms.widgets.PasswordInput,label="confirm password")

    def __init__(self,*a,**ka):
        super(AccountForm,self).__init__(*a,**ka)
        for i in self.fields:
            self.fields[i].widget.attrs.update({'class':'form-control'})
            self.fields[i].help_text =  ''


import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

class Visualisation():
	def draw_historical_plotly(self, region, dataset):
		cases = dataset[0].groupby(by=["Country/Region"], dropna=False).sum()
		deaths = dataset[1].groupby(by=["Country/Region"], dropna=False).sum()
		recovered = dataset[2].groupby(by=["Country/Region"], dropna=False).sum()
		cases = cases.reset_index()
		deaths = deaths.reset_index()
		recovered = recovered.reset_index()
		region_data = pd.DataFrame()
		region_data = region_data.append(cases[cases['Country/Region'] == region])
		region_data = region_data.append(deaths[deaths['Country/Region'] == region])
		region_data = region_data.append(recovered[recovered['Country/Region'] == region])
		region_data.reset_index(inplace=True)
		region_data.drop('index', axis = 1, inplace=True)
		region_data["Data type"] = ['Cases', 'Deaths', 'Recovered']
		region_data.set_index("Data type", inplace=True)

		fig = go.Figure()

		tmp = region_data[region_data.index == region_data.index[0]]
		fig.add_trace(go.Scatter(x=tmp.columns[4:], y=tmp[tmp.columns[4:]].loc['Cases'], mode='lines', name='Cases'))
		tmp = region_data[region_data.index == region_data.index[1]]
		fig.add_trace(go.Scatter(x=tmp.columns[4:], y=tmp[tmp.columns[4:]].loc['Deaths'], mode='lines', name='Deaths'))
		tmp = region_data[region_data.index == region_data.index[2]]
		fig.add_trace(go.Scatter(x=tmp.columns[4:], y=tmp[tmp.columns[4:]].loc['Recovered'], mode='lines', name='Recovered'))

		fig.update_layout(title='Historical plot for: '+region,
			xaxis_title='Date',
			yaxis_title='Number',
			template='plotly_dark')
		fig.update_xaxes(
			tickangle = 55)

		#fig.show()
		return fig

	def draw_historical_world_plotly(self, global_data):
		global_data = global_data

		fig = go.Figure()

		fig.add_trace(go.Scatter(x=global_data.index, y=global_data['total_cases'], mode='lines', name='Cases'))
		fig.add_trace(go.Scatter(x=global_data.index, y=global_data['total_deaths'], mode='lines', name='Deaths'))
		fig.add_trace(go.Scatter(x=global_data.index, y=global_data['total_recovered'], mode='lines', name='Recovered'))

		fig.update_layout(title='Historical world plot',
			xaxis_title='Date',
			yaxis_title='Number',
			template='plotly_dark')
		fig.update_xaxes(
			tickangle = 55)

		return fig

	def draw_world_map(self, world_map_data, crd_choice):
		if crd_choice == 'deaths':
			title = 'Global deaths map'
		elif crd_choice == 'recovered':
			title = 'Global recovered map'
		else:
			title = 'Global cases map'
		fig = go.Figure(data=go.Choropleth(
			locations=world_map_data['Country code'],
			z=world_map_data['Number'],
			#hover_name=world_map_data['Country/Region'],
			#color_continuous_scale=px.colors.sequential.Plasma,
			colorscale='Plasma',
			colorbar=dict(tickfont=dict(color='white')),
			hovertext=world_map_data['Country/Region'],
			#template ='plotly_dark')
			), layout = go.Layout(geo=dict(bgcolor= 'rgba(0,0,0,0)'),
				paper_bgcolor='#111111'))
		fig.update_layout(title=title, width=1200, height=800, font=dict(
        #size=18,
        color="white"
    ))

		return fig

	def draw_subplots(self, daily_bar_data, daily_table_data, crd_choice):
		x = daily_bar_data
		values = daily_table_data
		fig = make_subplots(column_widths=[0.8, 0.2], rows=1, cols=2, specs=[[{"type": "bar"},{"type":"table"}]])
		if crd_choice == 'deaths':
			y = x.iloc[4]
			title = 'Daily deaths'
		elif crd_choice == 'recovered':
			y = x.iloc[5]
			title = 'Daily recovered'
		else:
			y = x.iloc[3]
			title = 'Daily cases'
		fig.add_trace(go.Bar(x=x.iloc[3].index, y=y, marker_color='#FFFF00', name=''), row=1, col=1)
		fig.add_trace(go.Table(cells=dict(height=60, align=['center'],
				values=[['Yesterday\'s cases', 'Yesterday\'s deaths', 'Yesterday\'s recovered'], values])), row=1, col=2)
		fig.update_layout(title=title,
			xaxis_title='Date',
			yaxis_title='Number',
			template='plotly_dark')
		fig.layout['template']['data']['table'][0]['header']['fill']['color']='rgba(0,0,0,0)'
		fig.update_xaxes(
			tickangle = 55)

		return fig

	def draw_global_subplots(self, global_data, crd_choice):
		x = global_data
		values = global_data.iloc[:, [1,3,5]]
		fig = make_subplots(column_widths=[0.8, 0.2], rows=1, cols=2, specs=[[{"type": "bar"},{"type":"table"}]])
		if crd_choice == 'deaths':
			y = global_data['new_deaths']
			title = 'Daily global deaths'
		elif crd_choice == 'recovered':
			y = global_data['new_recovered']
			title = 'Daily global recovered'
		else:
			y = global_data['new_cases']
			title = 'Daily global cases'
		fig.add_trace(go.Bar(x=global_data.index, y=y, marker_color='#FFFF00', name=''), row=1, col=1)
		fig.add_trace(go.Table(cells=dict(height=60, align=['center'],
				values=[['Yesterday\'s cases', 'Yesterday\'s deaths', 'Yesterday\'s recovered'],
						[values['new_cases'][-1], values['new_deaths'][-1], values['new_recovered'][-1]]])), row=1, col=2)
		fig.update_layout(title=title,
			xaxis_title='Date',
			yaxis_title='Number',
			template='plotly_dark')
		fig.layout['template']['data']['table'][0]['header']['fill']['color']='rgba(0,0,0,0)'
		fig.update_xaxes(
			tickangle = 55)

		return fig


import uuid
from django.db import models
from django.utils.translation import gettext_lazy as _

from base.models.person import Person
from osis_notification.models.enums import (
    NotificationStates,
    NotificationTypes,
)

class Notification(models.Model):
    """Base class for a notification"""

    uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
    type = models.CharField(
        _("Type"),
        choices=NotificationTypes.choices(),
        max_length=25,
    )
    person = models.ForeignKey(
        Person,
        on_delete=models.CASCADE,
        related_name="+",
        null=True,
        blank=True,
    )
    payload = models.TextField(_("Payload"))
    state = models.CharField(
        _("State"),
        choices=NotificationStates.choices(),
        default=NotificationStates.PENDING_STATE.name,
        max_length=25,
    )

    created_at = models.DateTimeField(verbose_name=_("Created at"), auto_now_add=True)
    sent_at = models.DateTimeField(verbose_name=_("Sent at"), editable=False, null=True)
    read_at = models.DateTimeField(verbose_name=_("Read at"), editable=False, null=True)

    class Meta:
        constraints = [
            models.CheckConstraint(
                # A person cannot be null for WEB type
                check=~models.Q(
                    type=NotificationTypes.WEB_TYPE.name,
                    person_id__isnull=True,
                ),
                name='person_required_for_web',
            ),
        ]
        ordering = ["-created_at"]


from sklearn.neighbors import LocalOutlierFactor
import numpy as np
import pickle

LOF_N_NEIGHBORS = 5

def train_model(training_file_name, save_model=False):
    training_cases = np.genfromtxt(
        training_file_name,
        delimiter=',',
        skip_header=True
    )

    training_cases = training_cases[~np.isnan(training_cases).any(axis=1), :]

    lof_novelty = LocalOutlierFactor(
        n_neighbors=LOF_N_NEIGHBORS, novelty=True).fit(training_cases)

    if save_model:
        model_file = open('model.pickle', 'wb')
        pickle.dump(lof_novelty, model_file)

    return lof_novelty

if __name__ == "__main__":
    train_model("training_file_2.binetflow")


"""
Singleton basically makes every instance of specific class the same. Any changes made to any insance of a singleton class, will result in changes of every other object which has the same class.
"""

#Defining Metaclass
class CarMeta(type):

    _instances = {}

    #Making the object with same class instance
    def __call__(obj, *args, **kwargs):
        if obj not in obj._instances:
            instance = super().__call__(*args, **kwargs)
            obj._instances[obj] = instance
        return obj._instances[obj]

class CarModelX(metaclass=CarMeta):

    #For example, lets say the car has invalid amount of wheels from the beggining
    _wheels = 3

    def how_many_wheels(self):
        print(self._wheels, " wheels")

if __name__ == "__main__":

    CarOne = CarModelX()
    CarTwo = CarModelX()
    CarThree = CarModelX()

    #Lets check the amount of wheels:
    print("Car one has:")
    CarOne.how_many_wheels()

    print("Car two has:")
    CarTwo.how_many_wheels()

    print("Car three has:")
    CarThree.how_many_wheels()

    print("------------------")

    """
    Because we are using singleton pattern, we don't need to change wheel variable of every single class individually, we can do it with any CarModelX object instance.
    """

    CarOne._wheels = 4

    print("Car one has:")
    CarOne.how_many_wheels()

    print("Car two has:")
    CarTwo.how_many_wheels()

    print("Car three has:")
    CarThree.how_many_wheels()

    #We can make sure they contain the same instance by:
    print(id(CarOne), id(CarTwo), id(CarThree))


# Automatically adapted for numpy.oldnumeric Aug 01, 2007 by
# Further modified to be pure new numpy June 24th 2008

"""Bin index for non-rectilinear grids"""

from . import _bindex
import numpy

def bindexHorizontalGrid(latlin, lonlin):
    """Create a bin index for a horizontal grid.

    Parameters
    ----------
    latlin : latlin is the raveled latitude values.
    latlon : lonlin is the raveled longitude values.

    Returns
    -------
    resulting index.
    """
    lonlin = numpy.mod(lonlin, 360)
    NI, NJ = _bindex.getLens()
    # This should match NBINI, NBINJ in bindex.c
    head = numpy.zeros(NI * NJ, dtype='l')
    next = numpy.zeros(len(latlin), dtype='l')
    _bindex.bindex(latlin, lonlin, head, next)

    return (head, next)

def intersectHorizontalGrid(latspecs, lonspecs, latlin, lonlin, index):
    """Intersect a horizontal grid with a lat-lon region.

    Parameters
    ----------
    latspecs : latitude specs as defined in the grid module.
    lonspecs : longitude specs as defined in the grid module.
    latlin : latlin is the raveled latitude array.
    lonlin : lonlin is the raveled longitude array.
    index : index is the bin index as returned from bindex.

    Returns
    -------
    an array of indices, in latlin/lonlin, of the points in the intersection.
    """
    points = numpy.zeros(len(latlin), dtype='l')
    if latspecs is None:
        slat = -90.0
        elat = 90.0
        latopt = 'cc'
    else:
        slat = latspecs[0]
        elat = latspecs[1]
        latopt = latspecs[2]

    if slat > elat:
        tmp = slat
        slat = elat
        elat = tmp

    # If the longitude range is >=360.0, just intersect with the full range.
    # Otherwise, the points array could overflow and generate a seg fault.
    if lonspecs is None or abs(lonspecs[1] - lonspecs[0]) >= 360.0:
        slon = 0.0
        elon = 360.0
        lonopt = 'co'
    else:
        slon = lonspecs[0]
        elon = lonspecs[1]
        lonopt = lonspecs[2]

    if slon > elon:
        tmp = slon
        slon = elon
        elon = tmp

    npoints = _bindex.intersect(
        slat,
        slon,
        elat,
        elon,
        latlin,
        lonlin,
        index[0],
        index[1],
        points,
        latopt,
        lonopt)

    return points[:npoints]


import cv2
import numpy as np

def motion_blur(img, degree=10, angle=20):
    # 这里生成任意角度的运动模糊kernel的矩阵， degree越大，模糊程度越高
    M = cv2.getRotationMatrix2D((degree / 2, degree / 2), angle, 1)
    motion_blur_kernel = np.diag(np.ones(degree))
    motion_blur_kernel = cv2.warpAffine(motion_blur_kernel, M,
                                        (degree, degree))

    motion_blur_kernel = motion_blur_kernel / degree
    blurred = cv2.filter2D(img, -1, motion_blur_kernel)
    # convert to uint8
    cv2.normalize(blurred, blurred, 0, 255, cv2.NORM_MINMAX)
    blurred = np.array(blurred, dtype=np.uint8)
    return blurred

def defocus_blur(img, degree=10):
    return cv2.GaussianBlur(img, ksize=(degree, degree), sigmaX=0, sigmaY=0)

def circle_mask(size, center, radius, threshold=0):
    mask = np.zeros(size, dtype='uint8')
    mask = cv2.circle(mask, center, radius, 1, -1)
    if int(threshold) > 0:
        dist = cv2.distanceTransform((1 - mask).astype(np.uint8), cv2.DIST_L2, cv2.DIST_MASK_PRECISE)
        mask = np.exp(-np.square(dist) / (2 * np.square(threshold))) * (dist < 3 * threshold)
    return mask.astype(np.float32)

def random_mask(size):
    num_point = np.random.randint(1, 5)
    mask = np.zeros((num_point, *size))
    for idx in range(num_point):
        center = (np.random.randint(0, size[0]), np.random.randint(0, size[1]))
        mask[idx, :, :] = cv2.circle(mask[idx, :, :], center, np.random.randint(1, 6), 1, -1)
    mask = mask.sum(axis=0)
    mask = mask / np.maximum(mask.max(), 1)
    dist = cv2.distanceTransform((1 - mask).astype(np.uint8), cv2.DIST_L2, cv2.DIST_MASK_PRECISE)

    mask = np.exp(-np.square(dist) / (2 * np.square(50))) * (dist < 150)
    return mask

def contrast_and_brightness(img, alpha, beta):
    blank = np.zeros(img.shape, img.dtype)
    # dst = alpha * img + beta * blank
    dst = cv2.addWeighted(img, alpha, blank, 1 - alpha, beta)
    # dst = np.clip(img * alpha - beta, 0, 255)
    return dst

def imjpgcompress(img, num=10, q=60):
    for _ in range(num):
        _, encimg = cv2.imencode('.jpg', img, [cv2.IMREAD_IGNORE_ORIENTATION, q])
        img = cv2.imdecode(encimg, 0)
    return img

if __name__ == "__main__":
    img = cv2.imread('E:\\Dataset\\UniNet-test\\Image\\S2001L02.jpg',
                     cv2.IMREAD_GRAYSCALE)
    line = 'S2001L02, 0 242 349 48 248 349 96'
    line = [int(x) for x in line.split(' ')[1:]]
    center = (line[5], line[4])
    radius = line[6]
    # mask = random_mask(img.shape)
    # img = contrast_and_brightness(img, 5, 20) * mask + img * (1 - mask)
    img = imjpgcompress(img, num=10, q=5)
    cv2.imshow('img', img.astype(np.uint8))
    # mask = random_mask(img.shape)
    # cv2.imshow('img', mask)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


def main():
    luts = {
        "is_identifier": b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789",
        "is_identifier_start": b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_",
        "is_number": b"0123456789abcdefABCDEFuUlLxXpP.'",
        "is_number_start": b"0123456789",
    }
    for lut in luts.items():
        print(f"const u8 {lut[0]}[256] = {{")
        print("    ", end="")
        length = 0;
        for byte in range(0, 256):
            print(f"{int(byte in lut[1])}", end="")
            if byte != 255: print(", ", end="")
            length += 1
            if length % 10 == 0: print("\n    ", end="")
        print("\n};\n")

if __name__ == "__main__":
    main()


import math
from itertools import chain

import torch
from ppo_pytorch.common.make_grid import make_grid
from ppo_pytorch.actors.actors import ModularActor, ActorOutput
from ppo_pytorch.actors.utils import image_to_float, make_conv_heatmap
from torch import nn as nn, autograd as autograd
from torch.autograd import Variable

class GanCNNActor(ModularActor):
    """
    Convolution network.
    """

    def __init__(self, obs_space, action_space, head_factory, cnn_kind='large',
                 activation=nn.ReLU, actor_includes_conv=True, **kwargs):
        """
        Args:
            obs_space: Env's observation space
            action_space: Env's action space
            head_factory: Function which accept (hidden vector size, `ProbabilityDistribution`) and return `HeadBase`
            cnn_kind: Type of cnn.
                'small' - small CNN from arxiv DQN paper (Mnih et al. 2013)
                'large' - bigger CNN from Nature DQN paper (Mnih et al. 2015)
                'custom' - largest CNN of custom structure
            activation: Activation function
        """
        super().__init__(obs_space, action_space, **kwargs)
        self.activation = activation
        self.actor_includes_conv = actor_includes_conv
        assert cnn_kind in ('small', 'large', 'custom')

        ngf, ndf = 64, 64
        nz = 64
        nc = 4
        self.disc_start_net = nn.Sequential(
            nn.Sequential(
                nn.Conv2d(nc, ndf, 4, 2, 1, bias=True),
                nn.LeakyReLU(0.2, inplace=True),
            ),
            nn.Sequential(
                nn.Conv2d(ndf, ndf * 2, 4, 2, 0, bias=True),
                nn.LeakyReLU(0.2, inplace=True),
            ),
            nn.Sequential(
                nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=True),
                nn.LeakyReLU(0.2, inplace=True),
            ),
            nn.Sequential(
                nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 0, bias=True),
            ),
        )
        self.disc_end_net = nn.Sequential(
            nn.LeakyReLU(0.2),
            nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=True),
        )
        self.gen_start_net = nn.Sequential(
            nn.Conv2d(ndf * 8, nz, 4, 1, 0, bias=True),
        )
        self.gen_end_net = nn.Sequential(
            nn.Sequential(
                nn.ConvTranspose2d(nz, ngf * 8, 4, 1, 0, bias=False),
                nn.BatchNorm2d(ngf * 8),
                nn.LeakyReLU(0.2, inplace=True),
            ),
            nn.Sequential(
                nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 0, bias=False),
                nn.BatchNorm2d(ngf * 4),
                nn.LeakyReLU(0.2, inplace=True),
            ),
            nn.Sequential(
                nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False),
                nn.BatchNorm2d(ngf * 2),
                nn.LeakyReLU(0.2, inplace=True),
            ),
            nn.Sequential(
                nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 0, bias=False),
                nn.BatchNorm2d(ngf),
                nn.LeakyReLU(0.2, inplace=True),
            ),
            nn.Sequential(
                nn.ConvTranspose2d(ngf, nc, 4, 2, 1, bias=True),
            ),
        )

        # create head
        self.actor_linear = nn.Sequential(
            nn.Linear(nz, nz*2),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Linear(nz*2, nz*2),
            nn.LeakyReLU(0.2, inplace=True),
        )
        self.head = head_factory(nz*2, self.pd)

        self.actor_linear_head = nn.Sequential(
            self.actor_linear,
            self.head,
        )

        self.reset_weights()

    def params_disc(self):
        return chain(self.disc_start_net.parameters(), self.disc_end_net.parameters())

    def params_gen(self):
        return chain(self.gen_start_net.parameters(), self.gen_end_net.parameters())

    def params_head(self):
        return chain(self.head.parameters(), self.actor_linear.parameters())

    def params_actor(self):
        if self.actor_includes_conv:
            return chain(self.disc_start_net.parameters(), self.params_head())
        else:
            return self.params_head()

    def reset_weights(self):
        super().reset_weights()
        self.head.reset_weights()

    def forward_on_hidden_code(self, hidden):
        return self.actor_linear_head(hidden)

    def generate(self, hidden_code, state):
        x = hidden_code.contiguous().view(*hidden_code.shape, 1, 1)
        for i, conv in enumerate(self.gen_end_net):
            # run conv layer
            x = conv(x)

        # log
        if self.do_log:
            state = image_to_float(state.data)
            img = torch.cat([x.data.unsqueeze(2), state.unsqueeze(2)], 2)
            img = img[:min(img.shape[0], 2)]
            img = img.view(-1, 1, *img.shape[3:])
            img = img.clamp(-1, 1).div_(2).add_(0.5)
            img = make_grid(img, nrow=round(math.sqrt(img.shape[0])), normalize=False, fill_value=0.1)
            self.logger.add_image('reconstructed state', img, self._step)

        return x

    def discriminate(self, state, detach_hidden_from_conv=False):
        x = state
        for i, layer in enumerate(self.disc_start_net):
            # run conv layer
            x = layer(x)
            # log
            if self.do_log:
                self.log_conv_activations(i, layer[0], x)
                self.log_conv_filters(i, layer[0])

        # if self.do_log:
        #     self.logger.add_histogram('conv linear', x, self._step)

        conv_out = x
        disc = self.disc_end_net(x).view(-1)
        hidden = self.gen_start_net(x.detach() if detach_hidden_from_conv else x).view(x.shape[0], -1)
        return disc, hidden, conv_out

    def forward(self, input) -> ActorOutput:
        log_policy_attention = self.do_log and input.is_leaf
        input = image_to_float(input)
        if log_policy_attention:
            input = Variable(input.data, requires_grad=True)

        disc, hidden, conv_out = self.discriminate(input)

        # hidden = self.conv_actor_linear(conv_out)

        ac_out = self.actor_linear_head(hidden)
        ac_out.conv_out = conv_out
        ac_out.hidden_code = hidden
        ac_out.gan_d = disc

        if log_policy_attention:
            self.log_policy_attention(input, ac_out)

        return ac_out

    def log_conv_activations(self, index: int, conv: nn.Conv2d, x: Variable):
        img = x[0].data.unsqueeze(1).clone()
        img = make_conv_heatmap(img)
        img = make_grid(img, nrow=round(math.sqrt(conv.out_channels)), normalize=False, fill_value=0.1)
        self.logger.add_image('conv activations {} img'.format(index), img, self._step)
        self.logger.add_histogram('conv activations {} hist'.format(index), x[0], self._step)

    def log_conv_filters(self, index: int, conv: nn.Conv2d):
        channels = conv.in_channels * conv.out_channels
        shape = conv.weight.data.shape
        kernel_h, kernel_w = shape[2], shape[3]
        img = conv.weight.data.view(channels, 1, kernel_h, kernel_w).clone()
        max_img_size = 100 * 5
        img_size = channels * math.sqrt(kernel_h * kernel_w)
        if img_size > max_img_size:
            channels = channels * (max_img_size / img_size)
            channels = math.ceil(math.sqrt(channels)) ** 2
            img = img[:channels]
        img = make_conv_heatmap(img, scale=2 * img.std())
        img = make_grid(img, nrow=round(math.sqrt(channels)), normalize=False, fill_value=0.1)
        self.logger.add_image('conv featrues {} img'.format(index), img, self._step)
        self.logger.add_histogram('conv features {} hist'.format(index), conv.weight.data, self._step)

    def log_policy_attention(self, states, head_out):
        states_grad = autograd.grad(
            head_out.probs.abs().mean() + head_out.state_values.abs().mean(), states,
            only_inputs=True, retain_graph=True)[0]
        img = states_grad.data[:4]
        img.abs_()
        img /= img.view(4, -1).pow(2).mean(1).sqrt_().add_(1e-5).view(4, 1, 1, 1)
        img = img.view(-1, 1, *img.shape[2:]).abs()
        # img = make_conv_heatmap(img, scale=2*img.std())
        img = make_grid(img, 4, normalize=True, fill_value=0.1)
        self.logger.add_image('state attention', img, self._step)


#!/usr/bin/env python

import numpy as np
import torch

device = 'cuda' if torch.cuda.is_available() else 'cpu'

def get_epoch_data_loader(batch_size, forward_model,a, b,lambd_bd):
    x=torch.tensor(inverse_cdf_prior(np.random.uniform(size=(8*batch_size,3)),lambd_bd),dtype=torch.float,device=device)
    #x = torch.rand(8*batch_size, 3, device = device)*2-1
    y = forward_model(x)
    y += torch.randn_like(y) * b + torch.randn_like(y)*a*y
    def epoch_data_loader():
        for i in range(0, 8*batch_size, batch_size):
            yield x[i:i+batch_size].clone(), y[i:i+batch_size].clone()

    return epoch_data_loader

def get_log_posterior(samples, forward_model, a, b, ys,lambd_bd):
    relu=torch.nn.ReLU()
    forward_samps=forward_model(samples)
    prefactor = ((a*forward_samps)**2+b**2)
    p = .5*torch.sum(torch.log(prefactor), dim = 1)
    p2 =  0.5*torch.sum((ys-forward_samps)**2/prefactor, dim = 1)
    p3 = lambd_bd*torch.sum(relu(samples-1)+relu(-1-samples), dim = 1)
    return p+p2+p3

def inverse_cdf_prior(x,lambd_bd):
    x*=(2*lambd_bd+2)/lambd_bd
    y=np.zeros_like(x)
    left=x<1/lambd_bd
    y[left]=np.log(x[left]*lambd_bd)-1
    middle=np.logical_and(x>=1/lambd_bd,x < 2+1/lambd_bd)
    y[middle]=x[middle]-1/lambd_bd-1
    right=x>=2+1/lambd_bd
    y[right]=-np.log(((2+2/lambd_bd)-x[right])*lambd_bd)+1
    return y


from panda3d.core import *
from toontown.toonbase.ToonBaseGlobal import *
from .DistributedMinigame import *
from direct.interval.IntervalGlobal import *
from .OrthoWalk import *
from direct.showbase.PythonUtil import Functor, bound, lineupPos, lerp
from direct.fsm import ClassicFSM, State
from direct.fsm import State
from toontown.toonbase import TTLocalizer
from . import CatchGameGlobals
from direct.task.Task import Task
from toontown.toon import Toon
from toontown.suit import Suit
from . import MinigameAvatarScorePanel
from toontown.toonbase import ToontownTimer
from toontown.toonbase import ToontownGlobals
from . import CatchGameToonSD
from . import Trajectory
import math
from direct.distributed import DistributedSmoothNode
from direct.showbase.RandomNumGen import RandomNumGen
from . import MinigameGlobals
from toontown.toon import ToonDNA
from toontown.suit import SuitDNA
from .CatchGameGlobals import DropObjectTypes
from .CatchGameGlobals import Name2DropObjectType
from .DropPlacer import *
from .DropScheduler import *
from functools import reduce

class DistributedCatchGame(DistributedMinigame):
    DropTaskName = 'dropSomething'
    EndGameTaskName = 'endCatchGame'
    SuitWalkTaskName = 'catchGameSuitWalk'
    DropObjectPlurals = {'apple': TTLocalizer.CatchGameApples,
     'orange': TTLocalizer.CatchGameOranges,
     'pear': TTLocalizer.CatchGamePears,
     'coconut': TTLocalizer.CatchGameCoconuts,
     'watermelon': TTLocalizer.CatchGameWatermelons,
     'pineapple': TTLocalizer.CatchGamePineapples,
     'anvil': TTLocalizer.CatchGameAnvils}

    def __init__(self, cr):
        DistributedMinigame.__init__(self, cr)
        self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup')
        self.addChildGameFSM(self.gameFSM)
        self.setUsesSmoothing()
        self.setUsesLookAround()

    def getTitle(self):
        return TTLocalizer.CatchGameTitle

    def getInstructions(self):
        return TTLocalizer.CatchGameInstructions % {'fruit': self.DropObjectPlurals[self.fruitName],
         'badThing': self.DropObjectPlurals['anvil']}

    def getMaxDuration(self):
        return CatchGameGlobals.GameDuration + 5

    def load(self):
        self.notify.debug('load')
        DistributedMinigame.load(self)
        self.defineConstants()
        groundModels = ['phase_4/models/minigames/treehouse_2players',
         'phase_4/models/minigames/treehouse_2players',
         'phase_4/models/minigames/treehouse_3players',
         'phase_4/models/minigames/treehouse_4players']
        index = self.getNumPlayers() - 1
        self.ground = loader.loadModel(groundModels[index])
        self.ground.setHpr(180, -90, 0)
        self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow')
        self.dropObjModels = {}
        for objType in DropObjectTypes:
            if objType.name not in ['anvil', self.fruitName]:
                continue
            model = loader.loadModel(objType.modelPath)
            self.dropObjModels[objType.name] = model
            modelScales = {'apple': 0.7,
             'orange': 0.7,
             'pear': 0.5,
             'coconut': 0.7,
             'watermelon': 0.6,
             'pineapple': 0.45}
            if objType.name in modelScales:
                model.setScale(modelScales[objType.name])
            if objType == Name2DropObjectType['pear']:
                model.setZ(-.6)
            if objType == Name2DropObjectType['coconut']:
                model.setP(180)
            if objType == Name2DropObjectType['watermelon']:
                model.setH(135)
                model.setZ(-.5)
            if objType == Name2DropObjectType['pineapple']:
                model.setZ(-1.7)
            if objType == Name2DropObjectType['anvil']:
                model.setZ(-self.ObjRadius)
            model.flattenMedium()

        self.music = base.loader.loadMusic('phase_4/audio/bgm/MG_toontag.ogg')
        self.sndGoodCatch = base.loader.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.ogg')
        self.sndOof = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
        self.sndAnvilLand = base.loader.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.ogg')
        self.sndPerfect = base.loader.loadSfx('phase_4/audio/sfx/ring_perfect.ogg')
        self.toonSDs = {}
        avId = self.localAvId
        toonSD = CatchGameToonSD.CatchGameToonSD(avId, self)
        self.toonSDs[avId] = toonSD
        toonSD.load()
        if self.WantSuits:
            suitTypes = ['f',
             'tm',
             'pp',
             'dt']
            self.suits = []
            for type in suitTypes:
                suit = Suit.Suit()
                d = SuitDNA.SuitDNA()
                d.newSuit(type)
                suit.setDNA(d)
                suit.pose('walk', 0)
                self.suits.append(suit)

        self.__textGen = TextNode('ringGame')
        self.__textGen.setFont(ToontownGlobals.getSignFont())
        self.__textGen.setAlign(TextNode.ACenter)
        self.introMovie = self.getIntroMovie()

    def unload(self):
        self.notify.debug('unload')
        DistributedMinigame.unload(self)
        self.removeChildGameFSM(self.gameFSM)
        del self.gameFSM
        self.introMovie.finish()
        del self.introMovie
        del self.__textGen
        for avId in list(self.toonSDs.keys()):
            toonSD = self.toonSDs[avId]
            toonSD.unload()

        del self.toonSDs
        for suit in self.suits:
            suit.reparentTo(hidden)
            suit.delete()

        del self.suits
        self.ground.removeNode()
        del self.ground
        self.dropShadow.removeNode()
        del self.dropShadow
        for model in list(self.dropObjModels.values()):
            model.removeNode()

        del self.dropObjModels
        del self.music
        del self.sndGoodCatch
        del self.sndOof
        del self.sndAnvilLand
        del self.sndPerfect

    def getObjModel(self, objName):
        return self.dropObjModels[objName].copyTo(hidden)

    def __genText(self, text):
        self.__textGen.setText(text)
        return self.__textGen.generate()

    def calcDifficultyConstants(self, difficulty, numPlayers):
        ToonSpeedRange = [16.0, 25.0]
        self.ToonSpeed = lerp(ToonSpeedRange[0], ToonSpeedRange[1], difficulty)
        self.SuitSpeed = self.ToonSpeed / 2.0
        self.SuitPeriodRange = [lerp(5.0, 3.0, self.getDifficulty()), lerp(15.0, 8.0, self.getDifficulty())]

        def scaledDimensions(widthHeight, scale):
            w, h = widthHeight
            return [math.sqrt(scale * w * w), math.sqrt(scale * h * h)]

        BaseStageDimensions = [20, 15]
        areaScales = [1.0,
         1.0,
         3.0 / 2,
         4.0 / 2]
        self.StageAreaScale = areaScales[numPlayers - 1]
        self.StageLinearScale = math.sqrt(self.StageAreaScale)
        self.notify.debug('StageLinearScale: %s' % self.StageLinearScale)
        self.StageDimensions = scaledDimensions(BaseStageDimensions, self.StageAreaScale)
        self.notify.debug('StageDimensions: %s' % self.StageDimensions)
        self.StageHalfWidth = self.StageDimensions[0] / 2.0
        self.StageHalfHeight = self.StageDimensions[1] / 2.0
        MOHs = [24] * 2 + [26, 28]
        self.MinOffscreenHeight = MOHs[self.getNumPlayers() - 1]
        distance = math.sqrt(self.StageDimensions[0] * self.StageDimensions[0] + self.StageDimensions[1] * self.StageDimensions[1])
        distance /= self.StageLinearScale
        if self.DropPlacerType == PathDropPlacer:
            distance /= 1.5
        ToonRunDuration = distance / self.ToonSpeed
        offScreenOnScreenRatio = 1.0
        fraction = 1.0 / 3 * 0.85
        self.BaselineOnscreenDropDuration = ToonRunDuration / (fraction * (1.0 + offScreenOnScreenRatio))
        self.notify.debug('BaselineOnscreenDropDuration=%s' % self.BaselineOnscreenDropDuration)
        self.OffscreenTime = offScreenOnScreenRatio * self.BaselineOnscreenDropDuration
        self.notify.debug('OffscreenTime=%s' % self.OffscreenTime)
        self.BaselineDropDuration = self.BaselineOnscreenDropDuration + self.OffscreenTime
        self.MaxDropDuration = self.BaselineDropDuration
        self.DropPeriod = self.BaselineDropDuration / 2.0
        scaledNumPlayers = (numPlayers - 1.0) * 0.75 + 1.0
        self.DropPeriod /= scaledNumPlayers
        typeProbs = {'fruit': 3,
         'anvil': 1}
        probSum = reduce(lambda x, y: x + y, list(typeProbs.values()))
        for key in list(typeProbs.keys()):
            typeProbs[key] = float(typeProbs[key]) / probSum

        scheduler = DropScheduler(CatchGameGlobals.GameDuration, self.FirstDropDelay, self.DropPeriod, self.MaxDropDuration, self.FasterDropDelay, self.FasterDropPeriodMult)
        self.totalDrops = 0
        while not scheduler.doneDropping():
            scheduler.stepT()
            self.totalDrops += 1

        self.numFruits = int(self.totalDrops * typeProbs['fruit'])
        self.numAnvils = int(self.totalDrops - self.numFruits)

    def getNumPlayers(self):
        return self.numPlayers

    def defineConstants(self):
        self.notify.debug('defineConstants')
        self.DropPlacerType = RegionDropPlacer
        fruits = {ToontownGlobals.ToontownCentral: 'apple',
         ToontownGlobals.DonaldsDock: 'orange',
         ToontownGlobals.DaisyGardens: 'pear',
         ToontownGlobals.MinniesMelodyland: 'coconut',
         ToontownGlobals.TheBrrrgh: 'watermelon',
         ToontownGlobals.DonaldsDreamland: 'pineapple'}
        self.fruitName = fruits[self.getSafezoneId()]
        self.ShowObjSpheres = 0
        self.ShowToonSpheres = 0
        self.ShowSuitSpheres = 0
        self.PredictiveSmoothing = 1
        self.UseGravity = 1
        self.TrickShadows = 1
        self.WantSuits = 1
        self.FirstDropDelay = 0.5
        self.FasterDropDelay = int(2.0 / 3 * CatchGameGlobals.GameDuration)
        self.notify.debug('will start dropping fast after %s seconds' % self.FasterDropDelay)
        self.FasterDropPeriodMult = 0.5
        self.calcDifficultyConstants(self.getDifficulty(), self.getNumPlayers())
        self.notify.debug('ToonSpeed: %s' % self.ToonSpeed)
        self.notify.debug('total drops: %s' % self.totalDrops)
        self.notify.debug('numFruits: %s' % self.numFruits)
        self.notify.debug('numAnvils: %s' % self.numAnvils)
        self.ObjRadius = 1.0
        dropGridDimensions = [[5, 5],
         [5, 5],
         [6, 6],
         [7, 7]]
        self.DropRows, self.DropColumns = dropGridDimensions[self.getNumPlayers() - 1]
        self.cameraPosTable = [[0, -29.36, 28.17]] * 2 + [[0, -32.87, 30.43], [0, -35.59, 32.1]]
        self.cameraHpr = [0, -35, 0]
        self.CameraPosHpr = self.cameraPosTable[self.getNumPlayers() - 1] + self.cameraHpr
        for objType in DropObjectTypes:
            self.notify.debug('*** Object Type: %s' % objType.name)
            objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration
            self.notify.debug('onscreenDuration=%s' % objType.onscreenDuration)
            v_0 = 0.0
            t = objType.onscreenDuration
            x_0 = self.MinOffscreenHeight
            x = 0.0
            g = 2.0 * (x - x_0 - v_0 * t) / (t * t)
            self.notify.debug('gravity=%s' % g)
            objType.trajectory = Trajectory.Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.Trajectory.gravity))
            objType.fallDuration = objType.onscreenDuration + self.OffscreenTime

    def grid2world(self, column, row):
        x = column / float(self.DropColumns - 1)
        y = row / float(self.DropRows - 1)
        x = x * 2.0 - 1.0
        y = y * 2.0 - 1.0
        x *= self.StageHalfWidth
        y *= self.StageHalfHeight
        return (x, y)

    def showPosts(self):
        self.hidePosts()
        self.posts = [Toon.Toon(),
         Toon.Toon(),
         Toon.Toon(),
         Toon.Toon()]
        for i in range(len(self.posts)):
            toon = self.posts[i]
            toon.setDNA(base.localAvatar.getStyle())
            toon.reparentTo(render)
            x = self.StageHalfWidth
            y = self.StageHalfHeight
            if i > 1:
                x = -x
            if i % 2:
                y = -y
            toon.setPos(x, y, 0)

    def hidePosts(self):
        if hasattr(self, 'posts'):
            for toon in self.posts:
                toon.removeNode()

            del self.posts

    def showDropGrid(self):
        self.hideDropGrid()
        self.dropMarkers = []
        print('dropRows: %s' % self.DropRows)
        print('dropCols: %s' % self.DropColumns)
        for row in range(self.DropRows):
            self.dropMarkers.append([])
            rowList = self.dropMarkers[row]
            for column in range(self.DropColumns):
                toon = Toon.Toon()
                toon.setDNA(base.localAvatar.getStyle())
                toon.reparentTo(render)
                toon.setScale(1.0 / 3)
                x, y = self.grid2world(column, row)
                toon.setPos(x, y, 0)
                rowList.append(toon)

    def hideDropGrid(self):
        if hasattr(self, 'dropMarkers'):
            for row in self.dropMarkers:
                for marker in row:
                    marker.removeNode()

            del self.dropMarkers

    def onstage(self):
        self.notify.debug('onstage')
        DistributedMinigame.onstage(self)
        self.ground.reparentTo(render)
        self.scorePanels = []
        camera.reparentTo(render)
        camera.setPosHpr(*self.CameraPosHpr)
        lt = base.localAvatar
        lt.reparentTo(render)
        self.__placeToon(self.localAvId)
        lt.setSpeed(0, 0)
        toonSD = self.toonSDs[self.localAvId]
        toonSD.enter()
        toonSD.fsm.request('normal')
        self.orthoWalk.stop()
        radius = 0.7
        handler = CollisionHandlerEvent()
        handler.setInPattern('ltCatch%in')
        self.ltLegsCollNode = CollisionNode('catchLegsCollNode')
        self.ltLegsCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
        self.ltHeadCollNode = CollisionNode('catchHeadCollNode')
        self.ltHeadCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
        self.ltLHandCollNode = CollisionNode('catchLHandCollNode')
        self.ltLHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
        self.ltRHandCollNode = CollisionNode('catchRHandCollNode')
        self.ltRHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
        legsCollNodepath = lt.attachNewNode(self.ltLegsCollNode)
        legsCollNodepath.hide()
        head = base.localAvatar.getHeadParts().getPath(2)
        headCollNodepath = head.attachNewNode(self.ltHeadCollNode)
        headCollNodepath.hide()
        lHand = base.localAvatar.getLeftHands()[0]
        lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode)
        lHandCollNodepath.hide()
        rHand = base.localAvatar.getRightHands()[0]
        rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode)
        rHandCollNodepath.hide()
        lt.cTrav.addCollider(legsCollNodepath, handler)
        lt.cTrav.addCollider(headCollNodepath, handler)
        lt.cTrav.addCollider(lHandCollNodepath, handler)
        lt.cTrav.addCollider(lHandCollNodepath, handler)
        if self.ShowToonSpheres:
            legsCollNodepath.show()
            headCollNodepath.show()
            lHandCollNodepath.show()
            rHandCollNodepath.show()
        self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius))
        self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius))
        self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
        self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0))
        self.toonCollNodes = [legsCollNodepath,
         headCollNodepath,
         lHandCollNodepath,
         rHandCollNodepath]
        if self.PredictiveSmoothing:
            DistributedSmoothNode.activateSmoothing(1, 1)
        self.introMovie.start()

    def offstage(self):
        self.notify.debug('offstage')
        DistributedSmoothNode.activateSmoothing(1, 0)
        self.introMovie.finish()
        for avId in list(self.toonSDs.keys()):
            self.toonSDs[avId].exit()

        self.hidePosts()
        self.hideDropGrid()
        for collNode in self.toonCollNodes:
            while collNode.node().getNumSolids():
                collNode.node().removeSolid(0)

            base.localAvatar.cTrav.removeCollider(collNode)

        del self.toonCollNodes
        for panel in self.scorePanels:
            panel.cleanup()

        del self.scorePanels
        self.ground.reparentTo(hidden)
        DistributedMinigame.offstage(self)

    def handleDisabledAvatar(self, avId):
        self.notify.debug('handleDisabledAvatar')
        self.notify.debug('avatar ' + str(avId) + ' disabled')
        self.toonSDs[avId].exit(unexpectedExit=True)
        del self.toonSDs[avId]
        DistributedMinigame.handleDisabledAvatar(self, avId)

    def __placeToon(self, avId):
        toon = self.getAvatar(avId)
        idx = self.avIdList.index(avId)
        x = lineupPos(idx, self.numPlayers, 4.0)
        toon.setPos(x, 0, 0)
        toon.setHpr(180, 0, 0)

    def setGameReady(self):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameReady')
        if DistributedMinigame.setGameReady(self):
            return
        headCollNP = base.localAvatar.find('**/catchHeadCollNode')
        if headCollNP and not headCollNP.isEmpty():
            headCollNP.hide()
        for avId in self.remoteAvIdList:
            toon = self.getAvatar(avId)
            if toon:
                toon.reparentTo(render)
                self.__placeToon(avId)
                toonSD = CatchGameToonSD.CatchGameToonSD(avId, self)
                self.toonSDs[avId] = toonSD
                toonSD.load()
                toonSD.enter()
                toonSD.fsm.request('normal')
                toon.startSmooth()

    def setGameStart(self, timestamp):
        if not self.hasLocalToon:
            return
        self.notify.debug('setGameStart')
        DistributedMinigame.setGameStart(self, timestamp)
        self.introMovie.finish()
        camera.reparentTo(render)
        camera.setPosHpr(*self.CameraPosHpr)
        self.gameFSM.request('play')

    def enterOff(self):
        self.notify.debug('enterOff')

    def exitOff(self):
        pass

    def enterPlay(self):
        self.notify.debug('enterPlay')
        self.orthoWalk.start()
        for suit in self.suits:
            suitCollSphere = CollisionSphere(0, 0, 0, 1.0)
            suit.collSphereName = 'suitCollSphere%s' % self.suits.index(suit)
            suitCollSphere.setTangible(0)
            suitCollNode = CollisionNode(self.uniqueName(suit.collSphereName))
            suitCollNode.setIntoCollideMask(ToontownGlobals.WallBitmask)
            suitCollNode.addSolid(suitCollSphere)
            suit.collNodePath = suit.attachNewNode(suitCollNode)
            suit.collNodePath.hide()
            if self.ShowSuitSpheres:
                suit.collNodePath.show()
            self.accept(self.uniqueName('enter' + suit.collSphereName), self.handleSuitCollision)

        self.scores = [0] * self.numPlayers
        spacing = 0.4
        for i in range(self.numPlayers):
            avId = self.avIdList[i]
            avName = self.getAvatarName(avId)
            scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName)
            scorePanel.setScale(0.9)
            scorePanel.setPos(0.75 - spacing * (self.numPlayers - 1 - i), 0.0, 0.85)
            scorePanel.makeTransparent(0.75)
            self.scorePanels.append(scorePanel)

        self.fruitsCaught = 0
        self.droppedObjCaught = {}
        self.dropIntervals = {}
        self.droppedObjNames = []
        self.dropSchedule = []
        self.numItemsDropped = 0
        self.scheduleDrops()
        self.startDropTask()
        if self.WantSuits:
            self.startSuitWalkTask()
        self.timer = ToontownTimer.ToontownTimer()
        self.timer.posInTopRightCorner()
        self.timer.setTime(CatchGameGlobals.GameDuration)
        self.timer.countdown(CatchGameGlobals.GameDuration, self.timerExpired)
        self.timer.setTransparency(1)
        self.timer.setColorScale(1, 1, 1, 0.75)
        base.playMusic(self.music, looping=0, volume=0.9)

    def exitPlay(self):
        self.stopDropTask()
        self.stopSuitWalkTask()
        if hasattr(self, 'perfectIval'):
            self.perfectIval.pause()
            del self.perfectIval
        self.timer.stop()
        self.timer.destroy()
        del self.timer
        self.music.stop()
        for suit in self.suits:
            self.ignore(self.uniqueName('enter' + suit.collSphereName))
            suit.collNodePath.removeNode()

        for ival in list(self.dropIntervals.values()):
            ival.finish()

        del self.dropIntervals
        del self.droppedObjNames
        del self.droppedObjCaught
        del self.dropSchedule
        taskMgr.remove(self.EndGameTaskName)

    def timerExpired(self):
        pass

    def __handleCatch(self, objNum):
        self.notify.debug('catch: %s' % objNum)
        self.showCatch(self.localAvId, objNum)
        objName = self.droppedObjNames[objNum]
        objTypeId = CatchGameGlobals.Name2DOTypeId[objName]
        self.sendUpdate('claimCatch', [objNum, objTypeId])
        self.finishDropInterval(objNum)

    def showCatch(self, avId, objNum):
        isLocal = avId == self.localAvId
        objName = self.droppedObjNames[objNum]
        objType = Name2DropObjectType[objName]
        if objType.good:
            if objNum not in self.droppedObjCaught:
                if isLocal:
                    base.playSfx(self.sndGoodCatch)
                fruit = self.getObjModel(objName)
                toon = self.getAvatar(avId)
                rHand = toon.getRightHands()[0]
                self.toonSDs[avId].eatFruit(fruit, rHand)
        else:
            self.toonSDs[avId].fsm.request('fallForward')
        self.droppedObjCaught[objNum] = 1

    def setObjectCaught(self, avId, objNum):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() != 'play':
            self.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId))
            return
        isLocal = avId == self.localAvId
        if not isLocal:
            self.notify.debug('AI: avatar %s caught %s' % (avId, objNum))
            self.finishDropInterval(objNum)
            self.showCatch(avId, objNum)
        objName = self.droppedObjNames[objNum]
        if Name2DropObjectType[objName].good:
            i = self.avIdList.index(avId)
            self.scores[i] += 1
            self.scorePanels[i].setScore(self.scores[i])
            self.fruitsCaught += 1

    def finishDropInterval(self, objNum):
        if objNum in self.dropIntervals:
            self.dropIntervals[objNum].finish()

    def scheduleDrops(self):
        self.droppedObjNames = [self.fruitName] * self.numFruits + ['anvil'] * self.numAnvils
        self.randomNumGen.shuffle(self.droppedObjNames)
        dropPlacer = self.DropPlacerType(self, self.getNumPlayers(), self.droppedObjNames)
        while not dropPlacer.doneDropping():
            self.dropSchedule.append(dropPlacer.getNextDrop())

    def startDropTask(self):
        taskMgr.add(self.dropTask, self.DropTaskName)

    def stopDropTask(self):
        taskMgr.remove(self.DropTaskName)

    def dropTask(self, task):
        curT = self.getCurrentGameTime()
        while self.dropSchedule[0][0] <= curT:
            drop = self.dropSchedule[0]
            self.dropSchedule = self.dropSchedule[1:]
            dropTime, objName, dropCoords = drop
            objNum = self.numItemsDropped
            lastDrop = len(self.dropSchedule) == 0
            x, y = self.grid2world(*dropCoords)
            dropIval = self.getDropIval(x, y, objName, objNum)

            def cleanup(self = self, objNum = objNum, lastDrop = lastDrop):
                del self.dropIntervals[objNum]
                if lastDrop:
                    self.sendUpdate('reportDone')

            dropIval.append(Func(cleanup))
            self.dropIntervals[objNum] = dropIval
            self.numItemsDropped += 1
            dropIval.start(curT - dropTime)
            if lastDrop:
                return Task.done

        return Task.cont

    def setEveryoneDone(self):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() != 'play':
            self.notify.warning('ignoring setEveryoneDone msg')
            return
        self.notify.debug('setEveryoneDone')

        def endGame(task, self = self):
            if not CatchGameGlobals.EndlessGame:
                self.gameOver()
            return Task.done

        self.notify.debug('num fruits: %s' % self.numFruits)
        self.notify.debug('num catches: %s' % self.fruitsCaught)
        self.timer.hide()
        if self.fruitsCaught >= self.numFruits:
            self.notify.debug('perfect game!')
            perfectTextSubnode = hidden.attachNewNode(self.__genText(TTLocalizer.CatchGamePerfect))
            perfectText = hidden.attachNewNode('perfectText')
            perfectTextSubnode.reparentTo(perfectText)
            frame = self.__textGen.getCardActual()
            offsetY = -abs(frame[2] + frame[3]) / 2.0
            perfectTextSubnode.setPos(0, 0, offsetY)
            perfectText.setColor(1, 0.1, 0.1, 1)

            def fadeFunc(t, text = perfectText):
                text.setColorScale(1, 1, 1, t)

            def destroyText(text = perfectText):
                text.removeNode()

            textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(endGame, None))
            soundTrack = SoundInterval(self.sndPerfect)
            self.perfectIval = Parallel(textTrack, soundTrack)
            self.perfectIval.start()
        else:
            taskMgr.doMethodLater(1, endGame, self.EndGameTaskName)
        return

    def getDropIval(self, x, y, dropObjName, num):
        objType = Name2DropObjectType[dropObjName]
        dropNode = hidden.attachNewNode('catchDropNode%s' % num)
        dropNode.setPos(x, y, 0)
        shadow = self.dropShadow.copyTo(dropNode)
        shadow.setZ(0.2)
        shadow.setColor(1, 1, 1, 1)
        object = self.getObjModel(dropObjName)
        object.reparentTo(dropNode)
        if dropObjName in ['watermelon', 'anvil']:
            objH = object.getH()
            absDelta = {'watermelon': 12,
             'anvil': 15}[dropObjName]
            delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta
            newH = objH + delta
        else:
            newH = self.randomNumGen.random() * 360.0
        object.setH(newH)
        sphereName = 'FallObj%s' % num
        radius = self.ObjRadius
        if objType.good:
            radius *= lerp(1.0, 1.3, self.getDifficulty())
        collSphere = CollisionSphere(0, 0, 0, radius)
        collSphere.setTangible(0)
        collNode = CollisionNode(sphereName)
        collNode.setCollideMask(ToontownGlobals.CatchGameBitmask)
        collNode.addSolid(collSphere)
        collNodePath = object.attachNewNode(collNode)
        collNodePath.hide()
        if self.ShowObjSpheres:
            collNodePath.show()
        catchEventName = 'ltCatch' + sphereName

        def eatCollEntry(forward, collEntry):
            forward()

        self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, num)))

        def cleanup(self = self, dropNode = dropNode, num = num, event = catchEventName):
            self.ignore(event)
            dropNode.removeNode()

        duration = objType.fallDuration
        onscreenDuration = objType.onscreenDuration
        dropHeight = self.MinOffscreenHeight
        targetShadowScale = 0.3
        if self.TrickShadows:
            intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration)
            shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0))
            shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale))
        else:
            shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0)
        targetShadowAlpha = 0.4
        shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0))
        shadowIval = Parallel(shadowScaleIval, shadowAlphaIval)
        if self.UseGravity:

            def setObjPos(t, objType = objType, object = object):
                z = objType.trajectory.calcZ(t)
                object.setZ(z)

            setObjPos(0)
            dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration)
        else:
            startPos = Point3(0, 0, self.MinOffscreenHeight)
            object.setPos(startPos)
            dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn')
        ival = Sequence(Func(Functor(dropNode.reparentTo, render)), Parallel(Sequence(WaitInterval(self.OffscreenTime), dropIval), shadowIval), Func(cleanup), name='drop%s' % num)
        landSound = None
        if objType == Name2DropObjectType['anvil']:
            landSound = self.sndAnvilLand
        if landSound:
            ival.append(SoundInterval(landSound))
        return ival

    def startSuitWalkTask(self):
        ival = Parallel(name='catchGameMetaSuitWalk')
        rng = RandomNumGen(self.randomNumGen)
        delay = 0.0
        while delay < CatchGameGlobals.GameDuration:
            delay += lerp(self.SuitPeriodRange[0], self.SuitPeriodRange[0], rng.random())
            walkIval = Sequence(name='catchGameSuitWalk')
            walkIval.append(Wait(delay))

            def pickY(self = self, rng = rng):
                return lerp(-self.StageHalfHeight, self.StageHalfHeight, rng.random())

            m = [2.5,
             2.5,
             2.3,
             2.1][self.getNumPlayers() - 1]
            startPos = Point3(-(self.StageHalfWidth * m), pickY(), 0)
            stopPos = Point3(self.StageHalfWidth * m, pickY(), 0)
            if rng.choice([0, 1]):
                startPos, stopPos = stopPos, startPos
            walkIval.append(self.getSuitWalkIval(startPos, stopPos, rng))
            ival.append(walkIval)

        ival.start()
        self.suitWalkIval = ival

    def stopSuitWalkTask(self):
        self.suitWalkIval.finish()
        del self.suitWalkIval

    def getSuitWalkIval(self, startPos, stopPos, rng):
        data = {}
        lerpNP = render.attachNewNode('catchGameSuitParent')

        def setup(self = self, startPos = startPos, stopPos = stopPos, data = data, lerpNP = lerpNP, rng = rng):
            if len(self.suits) == 0:
                return
            suit = rng.choice(self.suits)
            data['suit'] = suit
            self.suits.remove(suit)
            suit.reparentTo(lerpNP)
            suit.loop('walk')
            suit.setPlayRate(self.SuitSpeed / ToontownGlobals.SuitWalkSpeed, 'walk')
            suit.setPos(0, 0, 0)
            lerpNP.setPos(startPos)
            suit.lookAt(stopPos)

        def cleanup(self = self, data = data, lerpNP = lerpNP):
            if 'suit' in data:
                suit = data['suit']
                suit.reparentTo(hidden)
                self.suits.append(suit)
            lerpNP.removeNode()

        distance = Vec3(stopPos - startPos).length()
        duration = distance / self.SuitSpeed
        ival = Sequence(FunctionInterval(setup), LerpPosInterval(lerpNP, duration, stopPos), FunctionInterval(cleanup))
        return ival

    def handleSuitCollision(self, collEntry):
        self.toonSDs[self.localAvId].fsm.request('fallBack')
        timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime())
        self.sendUpdate('hitBySuit', [self.localAvId, timestamp])

    def hitBySuit(self, avId, timestamp):
        if not self.hasLocalToon:
            return
        if self.gameFSM.getCurrentState().getName() != 'play':
            self.notify.warning('ignoring msg: av %s hit by suit' % avId)
            return
        toon = self.getAvatar(avId)
        if toon == None:
            return
        self.notify.debug('avatar %s hit by a suit' % avId)
        if avId != self.localAvId:
            self.toonSDs[avId].fsm.request('fallBack')
        return

    def enterCleanup(self):
        self.notify.debug('enterCleanup')

    def exitCleanup(self):
        pass

    def initOrthoWalk(self):
        self.notify.debug('startOrthoWalk')

        def doCollisions(oldPos, newPos, self = self):
            x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth)
            y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight)
            newPos.setX(x)
            newPos.setY(y)
            return newPos

        orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions)
        self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer())

    def destroyOrthoWalk(self):
        self.notify.debug('destroyOrthoWalk')
        self.orthoWalk.destroy()
        del self.orthoWalk

    def getIntroMovie(self):
        locNode = self.ground.find('**/locator_tree')
        treeNode = locNode.attachNewNode('treeNode')
        treeNode.setHpr(render, 0, 0, 0)

        def cleanupTree(treeNode = treeNode):
            treeNode.removeNode()

        initialCamPosHpr = (-0.21,
         -19.56,
         13.94,
         0.0,
         26.57,
         0.0)
        suitViewCamPosHpr = (0, -11.5, 13, 0, -35, 0)
        finalCamPosHpr = self.CameraPosHpr
        cameraIval = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, treeNode, *initialCamPosHpr), WaitInterval(4.0), LerpPosHprInterval(camera, 2.0, Point3(*suitViewCamPosHpr[:3]), Point3(*suitViewCamPosHpr[3:]), blendType='easeInOut', name='lerpToSuitView'), WaitInterval(4.0), LerpPosHprInterval(camera, 3.0, Point3(*finalCamPosHpr[:3]), Point3(*finalCamPosHpr[3:]), blendType='easeInOut', name='lerpToPlayView'))

        def getIntroToon(toonProperties, parent, pos):
            toon = Toon.Toon()
            dna = ToonDNA.ToonDNA()
            dna.newToonFromProperties(*toonProperties)
            toon.setDNA(dna)
            toon.reparentTo(parent)
            toon.setPos(*pos)
            toon.setH(180)
            toon.startBlink()
            return toon

        def cleanupIntroToon(toon):
            toon.detachNode()
            toon.stopBlink()
            toon.delete()

        def getThrowIval(toon, hand, object, leftToon, isAnvil = 0):
            anim = 'catch-intro-throw'
            grabFrame = 12
            fullSizeFrame = 30
            framePeriod = 1.0 / toon.getFrameRate(anim)
            objScaleDur = (fullSizeFrame - grabFrame) * framePeriod
            releaseFrame = 35
            trajDuration = 1.6
            trajDistance = 4
            if leftToon:
                releaseFrame = 34
                trajDuration = 1.0
                trajDistance = 1
            animIval = ActorInterval(toon, anim, loop=0)

            def getThrowDest(object = object, offset = trajDistance):
                dest = object.getPos(render)
                dest += Point3(0, -offset, 0)
                dest.setZ(0)
                return dest

            if leftToon:
                trajIval = ProjectileInterval(object, startVel=Point3(0, 0, 0), duration=trajDuration)
            else:
                trajIval = ProjectileInterval(object, endPos=getThrowDest, duration=trajDuration)
            trajIval = Sequence(Func(object.wrtReparentTo, render), trajIval, Func(object.wrtReparentTo, hidden))
            if isAnvil:
                trajIval.append(SoundInterval(self.sndAnvilLand))
            objIval = Track((grabFrame * framePeriod, Sequence(Func(object.reparentTo, hand), Func(object.setPosHpr, 0.05, -.13, 0.62, 0, 0, 336.8), LerpScaleInterval(object, objScaleDur, 1.0, startScale=0.1, blendType='easeInOut'))), (releaseFrame * framePeriod, trajIval))

            def cleanup(object = object):
                object.reparentTo(hidden)
                object.removeNode()

            throwIval = Sequence(Parallel(animIval, objIval), Func(cleanup))
            return throwIval

        tY = -4.0
        tZ = 19.5
        props = ['css',
         'md',
         'm',
         'f',
         9,
         0,
         9,
         9,
         13,
         5,
         11,
         5,
         8,
         7]
        leftToon = getIntroToon(props, treeNode, [-2.3, tY, tZ])
        props = ['mss',
         'ls',
         'l',
         'm',
         6,
         0,
         6,
         6,
         3,
         5,
         3,
         5,
         5,
         0]
        rightToon = getIntroToon(props, treeNode, [1.8, tY, tZ])
        fruit = self.getObjModel(self.fruitName)
        if self.fruitName == 'pineapple':
            fruit.setZ(0.42)
            fruit.flattenMedium()
        anvil = self.getObjModel('anvil')
        anvil.setH(100)
        anvil.setZ(0.42)
        anvil.flattenMedium()
        leftToonIval = getThrowIval(leftToon, leftToon.getRightHands()[0], fruit, leftToon=1)
        rightToonIval = getThrowIval(rightToon, rightToon.getLeftHands()[0], anvil, leftToon=0, isAnvil=1)
        animDur = leftToon.getNumFrames('catch-intro-throw') / leftToon.getFrameRate('catch-intro-throw')
        toonIval = Sequence(Parallel(Sequence(leftToonIval, Func(leftToon.loop, 'neutral')), Sequence(Func(rightToon.loop, 'neutral'), WaitInterval(animDur / 2.0), rightToonIval, Func(rightToon.loop, 'neutral')), WaitInterval(cameraIval.getDuration())), Func(cleanupIntroToon, leftToon), Func(cleanupIntroToon, rightToon))
        self.treeNode = treeNode
        self.fruit = fruit
        self.anvil = anvil
        self.leftToon = leftToon
        self.rightToon = rightToon
        introMovie = Sequence(Parallel(cameraIval, toonIval), Func(cleanupTree))
        return introMovie