#
#      Micro Marble is published under ...
#      ----------------------------------------------------------------------------
#      "THE TSCHUNK-WARE LICENSE" (Revision 23):
#      Niklas Roy wrote this file. As long as you retain this notice you
#      can do whatever you want with this stuff. If we meet some day, and you think
#      this stuff is worth it, you can buy me a tschunk in return
#      ----------------------------------------------------------------------------
#
import buttons
import display
import ledfx
import leds
import math
import bhi160
import utime
import power
import light_sensor
import vibra
import os
 
disp = display.open()

#orientation sensor variables
sensor = 0
sensors = [{"sensor": bhi160.BHI160Orientation(sample_rate=30), "name": "Orientation"}]
gx=0    #global tilt and turn values; reference is display x and y
gy=0
gz=0

p_gx=0
p_gy=0
p_gz=0

WIDTH = 159 # screen size
HEIGHT = 79


playfield = []

x_array_max = 0
y_array_max = 0
player_x    = 0     # position of player on playfield in pixels
player_y    = 0
new_player_x= 0
new_player_y= 0
player_origin_x = 0 # starting position of player in playfield (px)
player_origin_y = 0

playfield_color =(0,0,0)
start_color =(0,0,0)
playfield_bgnd = 0
max_time =0
level = 1
next_level = False
score = 0 # total score
bonus = 0 # score of this level
light_level = -250 # dims light in background by substracting -n...0
falling = 0 # falling counter starts at 0, kinda drops 1, goes on with 2
calib_x = 360
calib_y = 360
calib_z = 0
timeout_timer = 0
pause_count = 0
lifes = 0
clamp_count = 10 # ball clamp counter
extra_time_counter = 0
hiscore = 0 # highest score achieved in games (and saved in file)
hilifes = 0 # balls needed for highest score
totaltime=0

pf_col_lighted   = (0,0,0) # playfield colors under light
pf_start_lighted = (0,0,0)

BLOCK_SIZE=16
TRACK   = ord(':')
START   = ord('O')
GOAL    = ord('X')
VOID    = ord(' ')
EOF     = ord('E')
BOOST_U = ord('<')
BOOST_D = ord('>')
BOOST_L = ord('^')
BOOST_R = ord('v')
EXTRA_T = ord('t')
OSC_ST1 = ord('1')
OSC_ST2 = ord('2')
OSC_ST3 = ord('3')
OSC_ST4 = ord('4')
DROPPER = ord('d')
ICE     = ord('/')

teleporters_visible = 0                                         # number of teleporters in viewport
teleporters = [ [ 0 for i in range(2) ] for j in range(6) ]     # screen coordinates of teleporters in viewport
tele_block_x = [ 0 for i in range(6) ]                          # block coordinates of teleporters in playfield
tele_block_y = [ 0 for i in range(6) ] 
teleporting = 0
p_teleporting = 0
start_screen_count = 0
start_screen_b_speed = -35
start_screen_b_pos   = 45
start_screen_b_p_pos = 45
calib_count = 0
calib_return_count = 0
calib_return_ax = 0
calib_return_ay = 0
initial_gx = 0
initial_gy = 0
initial_gz = 0
p_initial_gx = 0
p_initial_gy = 0
p_initial_gz = 0
scroll_text = 0
scroll_y = 0

# buttons:
p_pressed = 0
pressed   = 0
button_top_right    = 0
button_bottom_right = 0
button_bottom_left  = 0

# game states:
START_SCREEN = 0
RUNNING      = 1
OVER         = 2
WON          = 3
TIMEOUT      = 4
PAUSED       = 5
CALIB        = 6

game = START_SCREEN
p_game = OVER
calib_return = START_SCREEN

title = ((1,1,1,1,1,0,2,0,3,3,3,3,0,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0),
         (1,0,1,0,1,0,0,0,3,0,0,0,0,4,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0),
         (1,0,1,0,1,0,2,0,3,0,0,0,0,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0),
         (1,0,0,0,1,0,2,0,3,0,0,0,0,4,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
         (1,0,0,0,1,0,2,0,3,3,3,3,0,4,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0),
         (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
         (5,5,5,5,5,0,6,6,6,6,0,7,7,7,7,0,8,0,0,0,0,9,0,0,0,0,1,1,1,1),
         (5,0,5,0,5,0,6,0,0,6,0,7,0,0,7,0,8,0,0,0,0,9,0,0,0,0,1,0,0,1),
         (5,0,5,0,5,0,6,6,6,6,0,7,7,7,7,0,8,8,8,8,0,9,0,0,0,0,1,1,1,1),
         (5,0,0,0,5,0,6,0,0,6,0,7,0,7,0,0,8,0,0,8,0,9,0,0,0,0,1,0,0,0),
         (5,0,0,0,5,0,6,0,0,6,0,7,0,0,7,0,8,8,8,8,0,9,9,9,9,0,1,1,1,1))

title_colors=((50,110,200),
              (50,170,140),
              (140,170,50),
              (200,80,50),
              (140,50,140),
              (200,50,110),
              (110,50,170),
              (80,140,170),
              (200,200,50))
alphabet=(
    ((0,1,1,1,0), # 0
     (1,0,0,0,1),
     (1,0,0,0,1),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((0,0,1,0,0), # 1
     (0,1,1,0,0),
     (1,0,1,0,0),
     (0,0,1,0,0),
     (1,1,1,1,1)),
    
    ((0,1,1,1,0), # 2
     (0,0,0,0,1),
     (0,1,1,1,0),
     (1,0,0,0,0),
     (1,1,1,1,1)),
    
    ((0,1,1,1,0), # 3
     (1,0,0,0,1),
     (0,0,1,1,0),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((1,0,0,0,0), # 4
     (1,0,1,0,0),
     (1,1,1,1,1),
     (0,0,1,0,0),
     (0,0,1,0,0)),
    
    ((1,1,1,1,1), # 5
     (1,0,0,0,0),
     (1,1,1,1,0),
     (0,0,0,0,1),
     (0,1,1,1,0)),
    
    ((0,1,1,1,1), # 6
     (1,0,0,0,0),
     (1,1,1,1,0),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((1,1,1,1,1), # 7
     (0,0,0,1,0),
     (0,0,1,0,0),
     (0,1,0,0,0),
     (1,0,0,0,0)),
    
    ((0,1,1,1,0), # 8
     (1,0,0,0,1),
     (0,1,1,1,0),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((0,1,1,1,0), # 9
     (1,0,0,0,1),
     (0,1,1,1,1),
     (0,0,0,0,1),
     (0,1,1,1,0)),

    ((0,0,0,0,0), # +
     (0,0,1,0,0),
     (0,1,1,1,0),
     (0,0,1,0,0),
     (0,0,0,0,0)),
    
    ((0,0,1,0,0), # A
     (0,1,0,1,0),
     (1,1,1,1,1),
     (1,0,0,0,1),
     (1,0,0,0,1)),
    
    ((1,1,1,1,0), # B
     (1,0,0,0,1),
     (1,1,1,1,0),
     (1,0,0,0,1),
     (1,1,1,1,0)),
    
    ((0,1,1,1,0), # C
     (1,0,0,0,1),
     (1,0,0,0,0),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((1,1,1,1,0), # D
     (1,0,0,0,1),
     (1,0,0,0,1),
     (1,0,0,0,1),
     (1,1,1,1,0)),
    
    ((1,1,1,1,1), # E
     (1,0,0,0,0),
     (1,1,1,1,0),
     (1,0,0,0,0),
     (1,1,1,1,1)),
    
    ((1,1,1,1,1), # F
     (1,0,0,0,0),
     (1,1,1,1,0),
     (1,0,0,0,0),
     (1,0,0,0,0)),
    
    ((0,1,1,1,0), # G
     (1,0,0,0,0),
     (1,0,1,1,1),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((1,0,0,0,1), # H
     (1,0,0,0,1),
     (1,1,1,1,1),
     (1,0,0,0,1),
     (1,0,0,0,1)),
    
    ((1,1,1,1,1), # I
     (0,0,1,0,0),
     (0,0,1,0,0),
     (0,0,1,0,0),
     (1,1,1,1,1)),
    
    ((1,1,1,1,1), # J
     (0,0,0,0,1),
     (0,0,0,0,1),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((1,0,0,0,1), # K
     (1,0,0,1,0),
     (1,1,1,1,1),
     (1,0,0,0,1),
     (1,0,0,0,1)),
    
    ((1,0,0,0,0), # L
     (1,0,0,0,0),
     (1,0,0,0,0),
     (1,0,0,0,0),
     (1,1,1,1,1)),
    
    ((1,0,0,0,1), # M
     (1,1,0,1,1),
     (1,0,1,0,1),
     (1,0,0,0,1),
     (1,0,0,0,1)),
    
    ((1,0,0,0,1), # N 
     (1,1,0,0,1),
     (1,0,1,0,1),
     (1,0,0,1,1),
     (1,0,0,0,1)),
    
    ((0,1,1,1,0), # O
     (1,0,0,0,1),
     (1,0,0,0,1),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((1,1,1,1,0), # P
     (1,0,0,0,1),
     (1,1,1,1,0),
     (1,0,0,0,0),
     (1,0,0,0,0)),
    
    ((0,1,1,1,0), # Q
     (1,0,0,0,1),
     (1,0,1,0,0),
     (1,0,0,1,0),
     (0,1,1,0,1)),
    
    ((1,1,1,1,0), # R
     (1,0,0,0,1),
     (1,1,1,1,0),
     (1,0,0,1,0),
     (1,0,0,0,1)),
    
    ((0,1,1,1,1), # S
     (1,0,0,0,0),
     (0,1,1,1,0),
     (0,0,0,0,1),
     (1,1,1,1,0)),
    
    ((1,1,1,1,1), # T
     (0,0,1,0,0),
     (0,0,1,0,0),
     (0,0,1,0,0),
     (0,0,1,0,0)),
    
    ((1,0,0,0,1), # U
     (1,0,0,0,1),
     (1,0,0,0,1),
     (1,0,0,0,1),
     (0,1,1,1,0)),
    
    ((1,0,0,0,1), # V
     (1,0,0,0,1),
     (0,1,0,1,0),
     (0,1,0,1,0),
     (0,0,1,0,0)),
    
    ((1,0,0,0,1), # W
     (1,0,0,0,1),
     (1,0,1,0,1),
     (1,1,0,1,1),
     (1,0,0,0,1)),
    
    ((1,0,0,0,1), # X
     (0,1,0,1,0),
     (0,0,1,0,0),
     (0,1,0,1,0),
     (1,0,0,0,1)),
    
    ((1,0,0,0,1), # Y
     (0,1,0,1,0),
     (0,0,1,0,0),
     (0,0,1,0,0),
     (0,0,1,0,0)),
    
    ((1,1,1,1,1), # Z
     (0,0,0,1,0),
     (0,0,1,0,0),
     (0,1,0,0,0),
     (1,1,1,1,1)),
    
    ((0,1,0,0,0), # !
     (0,1,0,0,0),
     (0,1,0,0,0),
     (0,0,0,0,0),
     (0,1,0,0,0)),
    
    ((0,0,0,0,0), # :
     (0,1,0,0,0),
     (0,0,0,0,0),
     (0,1,0,0,0),
     (0,0,0,0,0)),
    
    ((0,0,0,0,0), # ' '
     (0,0,0,0,0),
     (0,0,0,0,0),
     (0,0,0,0,0),
     (0,0,0,0,0)),
    
    ((0,0,0,0,0), # .
     (0,0,0,0,0),
     (0,0,0,0,0),
     (0,0,0,0,0),
     (1,0,0,0,0)),
    
    ((0,0,0,0,0), # -
     (0,0,0,0,0),
     (1,1,1,1,1),
     (0,0,0,0,0),
     (0,0,0,0,0)),

    ((0,0,0,0,0), # /
     (0,0,0,1,0),
     (0,0,1,0,0),
     (0,1,0,0,0),
     (0,0,0,0,0)),
    
    ((0,0,0,0,0), # +
     (0,0,1,0,0),
     (0,1,1,1,0),
     (0,0,1,0,0),
     (0,0,0,0,0)))

#-------------------------------------------------------------- game engine
def game_engine():

    global game
    global extra_time_counter
    global clamp_count
    global falling
    global light_level
    global led_color
    global loop_count
    global led_timer
    global led_unit
    global p_led_timer
    global warning
    global lifes
    global player_x
    global player_y
    global player_x_speed
    global player_y_speed
    global p_player_block
    global new_player_x
    global new_player_y
    global player_block_x
    global p_player_block_x
    global player_block_y
    global p_player_block_y
    global block_below
    global p_block_below
    global playfield
    global player_on_blocks
    
    global teleporting
    global p_teleporting
    global tele_dest_index
    global tele_dest_x
    global tele_dest_y
    global tele_orig_x
    global tele_orig_y
    global tele_distance
    global tele_block_below

    global max_time
    global extra_time_counter
    global bonus

    global winning 
    global winning_time

    global x_player_screen
    global y_player_screen
    global show_bonus_timer
    global pf_start_lighted

    loop_count+=1
    
    #.................................... extra time effect counter
    if extra_time_counter>0:
        extra_time_counter-=1;
                
    #.................................... clamp player
    if clamp_count > 0:
        clamp_count-=1
            
    #....................................................... light fading / general playfield lighting / light_level goes from 0=normal light to -255=full dark, playfield renderer off
                    
    if falling>=3:  #                                        fade light out to -255
        light_level = constrain(int((-256 + light_level*7)/8),-255,0)
                
    if game==WON or game==OVER or game==TIMEOUT :   #        dim light down
        light_level = int((-50 + light_level*2)/3)

    elif game==PAUSED: #                                     switch light off
        light_level=-255
                
    elif game==RUNNING: #                                    fade light in
        if light_level<0:
            light_level = constrain(int(light_level/3*2+1),-255,0)

    #.................................... ambient leds
                    
    if game==WON:
                
        led_color = (  constrain(pf_start_lighted[0] +10-winning*2,0,255),
                        constrain(pf_start_lighted[1] +10-winning*2,0,255),
                        constrain(pf_start_lighted[2] +10-winning*2,0,255))
                
        for led in range(11,15):
            leds.prep(led, led_color)

    else:
        for led in range(11,15):    # ambient effect reset
            leds.prep(led, (0,0,0))
                    
    #.................................... timer / led bar 
                    

    if game==RUNNING:
        led_unit    = max_time//11
        p_led_timer = led_timer
        led_timer   = 0
        warning     = 0
                
        if p_led_timer < 2:         # blink when little time is left

            led_h =int (((max_time - loop_count)*2)%led_unit/led_unit*60) # create led blink color
                    
            led_color=(constrain(playfield_color[0]+led_h-30,0,255),
                        constrain(playfield_color[1]+led_h-30,0,255),
                        constrain(playfield_color[2]+led_h-30,0,255))

            warning =int (((max_time - loop_count)*2)%led_unit/led_unit*100) # create synchronized warning oscillator
                    
                    
        else:
            led_color = playfield_color
                
        for led in range(11):
                    
            if led_unit * led > max_time - loop_count or show_bonus_timer > led:
                leds.prep(led, (0,0,0))
            else:
                leds.prep(led, led_color)
                if(extra_time_counter>0):
                    if abs(12-(extra_time_counter*3)-led)<2:
                        leds.prep(led, start_color)
                            
                led_timer = led

        if led_timer < 2 and warning==0:
            vibra.vibrate(25)



        if max_time < loop_count:
            game=TIMEOUT
            lifes += 1
            vibra.vibrate(250)
                


    #.................................... calculate speed of player
    if falling==0 and winning==0 and game!=TIMEOUT:       # normal game

        slip=0
        if block_below == ICE:
            slip=20
                
        player_x_speed = (player_x_speed*(2+slip) + gx/2)/(3.2+slip)
        player_y_speed = (player_y_speed*(2+slip) + gy/2)/(3.2+slip)

    if falling==2:                      # starting to fall: move away from center of last block
                
        if abs(p_player_block_x*BLOCK_SIZE+BLOCK_SIZE//2 - player_x) > abs(p_player_block_y*BLOCK_SIZE+BLOCK_SIZE//2 - player_y):
            if player_x_speed>0:
                player_x_speed+=1
            else:
                player_x_speed-=1
        else: 
            if player_y_speed>0:
                player_y_speed+=1
            else:
                player_y_speed-=1
                        

    #.................................... calculate potential position of player in new loop
    if falling<20 and winning==0:

        new_player_x = player_x + constrain(player_x_speed,-BLOCK_SIZE/2+.1,BLOCK_SIZE/2-.1) # center of player in pixels on playfield
        new_player_y = player_y + constrain(player_y_speed,-BLOCK_SIZE/2+.1,BLOCK_SIZE/2-.1)


                

    #----------------------------------------------------------------------------------------------------------------            
    #----------------------------------------------------------------------------------- S U R F A C E detection
    #----------------------------------------------------------------------------------------------------------------

    if falling==0 and winning==0 and teleporting==0:

                    
        p_player_block_x = player_block_x # previous block coordinates of player
        p_player_block_y = player_block_y
                    
        player_has_moved = False


        player_block_x = int(new_player_x)//BLOCK_SIZE                                  # block coordinates of player
        player_block_y = int(new_player_y)//BLOCK_SIZE

        player_hblock_x = int(new_player_x-BLOCK_SIZE/2)//BLOCK_SIZE                    # half grid coordinates of player (shift grid by half blocksize)
        player_hblock_y = int(new_player_y-BLOCK_SIZE/2)//BLOCK_SIZE

        p_block_below = block_below                                                     # remember block of previous loop
        block_below = VOID
                                                                                        # content of block below player
        if ( player_block_x >= 0 and player_block_x < x_array_max and player_block_y >= 0 and player_block_y < y_array_max ) :
            block_below=playfield[player_block_x][player_block_y]

        if(p_player_block_x != player_block_x or p_player_block_y != player_block_y):   # moved to new tile?
            player_has_moved = True                                                     # then tick the motor
            if (p_block_below != DROPPER or p_block_below != DROPPER+1) and block_below==DROPPER:
                vibra.vibrate(7) # dropping 1 px down
            elif (p_block_below==DROPPER or p_block_below==DROPPER+1) and block_below!=DROPPER:
                vibra.vibrate(6) # jumping up 1 px
            else:
                vibra.vibrate(4) # crossing tiles                                                           


    #.................................... teleporter
    p_teleporting = teleporting

    if game==TIMEOUT:
        teleporting   = 0
        p_teleporting = 0
                        
    if ((block_below>=65 and block_below<=67) or (block_below>=97 and block_below<=99)):
        if tele_block_below==False: # happens once when player enters tele block

            tele_block_below = True

            if teleporting==0:         # teleporter: ABC,abc
                        
                teleporting = 1
                tele_dest_index = 0

                if (block_below<97):
                    tele_dest_index = block_below - 62    
                else:
                    tele_dest_index = block_below - 97

                tele_dest_x = tele_block_x[tele_dest_index]* BLOCK_SIZE + BLOCK_SIZE//2   # 0:A, 1:B, 2:C, 3:a, 4:b, 5:c
                tele_dest_y = tele_block_y[tele_dest_index]* BLOCK_SIZE + BLOCK_SIZE//2
                        
                tele_orig_x = new_player_x # center of this teleporter in px
                tele_orig_y = new_player_y

                tele_distance = int(math.sqrt((tele_dest_x-tele_orig_x)**2 + (tele_dest_y-tele_orig_y)**2)/10)
                if (tele_distance<3):
                    tele_distance=3
    else:
        tele_block_below = False

            # . . . . . . . . . . . . . . . . . . teleporting
                
    if teleporting>0:    # happens during teleporting
        teleporting +=1;

        if teleporting  <= tele_distance:   # moving to destination teleporter
            tele_move    = teleporting/tele_distance
            new_player_x = int(tele_dest_x * tele_move + tele_orig_x *(1-tele_move))
            new_player_y = int(tele_dest_y * tele_move + tele_orig_y *(1-tele_move))

        else:
            teleporting=0                   # reset teleport counter for next teleportation
                
                            
                                 
                    
    #.................................... booster
    if block_below==BOOST_U:
        player_y_speed-=55
        vibra.vibrate(15)
    elif block_below==BOOST_D:
        player_y_speed+=55
        vibra.vibrate(15)
    elif block_below==BOOST_L:
        player_x_speed-=55
        vibra.vibrate(15)
    elif block_below==BOOST_R:
        player_x_speed+=55
        vibra.vibrate(15)

    #.................................... osc stage
    elif block_below>=OSC_ST1 and block_below<=OSC_ST4:
        block_i = block_below - OSC_ST1
        block_osc = abs((((loop_count//15)+block_i)%4)-2)
        if block_osc==0:
            block_below = VOID
            player_on_blocks = False

    #.................................... dropper
    elif block_below >= DROPPER and block_below <=DROPPER+7:
                    

        if block_below==DROPPER:
            playfield[player_block_x][player_block_y] = DROPPER+1
                        
        elif block_below>DROPPER+1:
            block_below = VOID
            player_on_blocks = False
                
    #.................................... extra time
    elif block_below==EXTRA_T and game!=TIMEOUT:
                    
        vibra.vibrate(12)
        playfield[player_block_x][player_block_y]=TRACK
        max_time += 50
        extra_time_counter = 5

    #.................................... void? -> falling
    if block_below==VOID and falling==0 and winning==0:
                    
        falling=1
        bonus = 0
        lifes +=1
                    
        vibra.vibrate(10)
    if falling >0  and (game==RUNNING or game==TIMEOUT or game==OVER):
        falling+=1
                    
    if falling==20:
        vibra.vibrate(20)

    #.................................... goal reached? -> winning
    if block_below==GOAL and winning==0 and game!=TIMEOUT:
                    
        winning = 1 # winning state counter, frame based 
        winning_time = loop_count
        bonus = 0
                    
    if winning>0 and game!= PAUSED:
        winning+=1
        player_x_speed=0
        player_y_speed=0

        this_goal_x = player_block_x*BLOCK_SIZE+BLOCK_SIZE//2
        this_goal_y = player_block_y*BLOCK_SIZE+BLOCK_SIZE//2
                    
        if abs(player_x-this_goal_x)>1:
            if player_x > this_goal_x:
                new_player_x-=1
            if player_x < this_goal_x:
                new_player_x+=1
        else:
            player_x = this_goal_x
                        
        if abs(player_y-this_goal_y)>1:
            if player_y > this_goal_y:
                new_player_y-=1
            if player_y < this_goal_y:
                new_player_y+=1
        else:
            player_y = this_goal_y
                        
                        
    if winning==9:
        vibra.vibrate(8)
    if winning==11:
        vibra.vibrate(10)
    if winning==12:
        vibra.vibrate(25)
                    
                    
    if winning>10:
        game = WON
        
    #.................................... update player
    if clamp_count==0:
        player_x = new_player_x
        player_y = new_player_y
            
    if falling>0:
        if abs(p_player_block_x*BLOCK_SIZE+BLOCK_SIZE//2 - player_x) > BLOCK_SIZE:
            player_on_blocks=False
        if abs(p_player_block_y*BLOCK_SIZE+BLOCK_SIZE//2 - player_y) > BLOCK_SIZE:
            player_on_blocks=False

                    
    #.................................... calculate and render playfield ( only if light is on )       
    if light_level>-250:

        #.................................... calculate viewport in screen and array

                    
        pf_orig_x = - player_x + WIDTH/2  - constrain(int(gx),-65,65)     # calculate origin of playfield relative to screen origin
        pf_orig_y = - player_y + HEIGHT/2 - constrain(int(gy),-40,40)
         

        x_fine_shift = int(pf_orig_x) % BLOCK_SIZE      # shift for fine scrolling
        y_fine_shift = int(pf_orig_y) % BLOCK_SIZE      #


            
        #.................................... draw background
        bgnd_col=constrain(40+(light_level//5),0,40)
        for bx in range(int(pf_orig_x/3)%12,WIDTH,12):
            disp.line(bx,0,bx,HEIGHT,col=(bgnd_col,bgnd_col,bgnd_col))
        for by in range(int(pf_orig_y/3)%12,HEIGHT,12):
            disp.line(0,by,WIDTH,by,col=(bgnd_col,bgnd_col,bgnd_col))
                    
        #.................................... calculate player

        x_player_screen = constrain(int(pf_orig_x + player_x),0,WIDTH+1) # in pixels
        y_player_screen = constrain(int(pf_orig_y + player_y),0,HEIGHT+1)

        if p_block_below >= DROPPER and p_block_below<=DROPPER+7:
            falling_gfx = constrain(falling//4+1,1,6)
        else:
            falling_gfx = constrain(falling//4,0,6)
                
        #.................................... draw player background (if falling)
        if player_on_blocks==False:
            draw_player(x_player_screen,y_player_screen,falling_gfx,warning,clamp_count)


        #.................................... calculate playfield

        b=abs((loop_count*40)%600-300)-150 # sawtooth oscillator +-75
        c1= (constrain(start_color[0]+b+light_level,0,255),constrain(start_color[1]+b+light_level,0,255),constrain(start_color[2]+b+light_level,0,255))
        c2= (constrain(start_color[0]-b+light_level,0,255),constrain(start_color[1]-b+light_level,0,255),constrain(start_color[2]-b+light_level,0,255))

        teleporters_visible = 0
                    
        for x in range(-1,WIDTH//BLOCK_SIZE+1):
            for y in range(-1,HEIGHT//BLOCK_SIZE+1): #l-r

                x_array = x - int( pf_orig_x) // BLOCK_SIZE
                y_array = y - int( pf_orig_y) // BLOCK_SIZE

                block = VOID        
                                       
                if ( x_array >= 0 and x_array < x_array_max and y_array >= 0 and y_array < y_array_max ) :
                    block = playfield[x_array][y_array]
                                       
                if block != VOID :
                    x_start = x * BLOCK_SIZE + x_fine_shift              # start and end position of block in pixels on screen
                    x_end   = x * BLOCK_SIZE + x_fine_shift + BLOCK_SIZE -2       
                    y_start = y * BLOCK_SIZE + y_fine_shift            
                    y_end   = y * BLOCK_SIZE + y_fine_shift + BLOCK_SIZE -2

                    x_start_c = constrain(x_start, 0 , WIDTH)            # start and end position constrained to screen limits
                    x_end_c   = constrain(x_end  , 0 , WIDTH)           
                    y_start_c = constrain(y_start, 0 , HEIGHT)                
                    y_end_c   = constrain(y_end  , 0 , HEIGHT)  
                    #...................................................................................................
                    #...................................................................  D R A W   P L A Y F I E L D  .
                    #...................................................................................................
                            
                    #.................................................................... lighted playfield_colors 
                    pf_col_lighted   = (constrain(playfield_color[0]+light_level,0,255),
                                        constrain(playfield_color[1]+light_level,0,255),
                                        constrain(playfield_color[2]+light_level,0,255))
                            
                    pf_start_lighted = (constrain(start_color[0]+light_level,0,255),
                                        constrain(start_color[1]+light_level,0,255),
                                        constrain(start_color[2]+light_level,0,255))
                            
                    #.................................................................... TILE/TRACK
                            
                    if block == TRACK:
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_col_lighted  , filled=True)
                                
                    #.................................................................... EXTRA TIME
                    elif block == EXTRA_T:
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_col_lighted  , filled=True)

                        hlx=constrain(-int(gx),-1,1)                # highlight / shadow offset based on gyro sensor
                        hly=constrain(-int(gy),-1,1)

                        xh =x_start  + BLOCK_SIZE//2 
                        yh =y_start  + BLOCK_SIZE//2-1
                                
                        ll=int(light_level/2)

                        tx = x_start_c+BLOCK_SIZE//2-2+hlx
                        ty = y_start_c+BLOCK_SIZE//2+2+hly
                        if (tx>=0 and tx<=WIDTH and ty>=0 and ty<=HEIGHT):
                            disp.circ(tx,ty, rad=3, col = (0,0,0), filled=True)     #shadow

                        tx = xh
                        ty = yh
                        if (tx>=0 and tx<=WIDTH and ty>=0 and ty<=HEIGHT):
                            cl = constrain(190 + ll,0,255)
                            disp.circ(tx,ty, rad=3, col = (cl,cl,cl), filled=True)  # little ball
                                    
                    #.................................................................... BOOSTER

                    elif block == BOOST_D:
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_col_lighted  , filled=True)
                        for i in range(1,6,3):
                            disp.line(constrain( x_start +7  -i  , 0 , WIDTH),
                                        constrain( y_start +10 -i  , 0 , HEIGHT),
                                        constrain( x_start +7  +i  , 0 , WIDTH),
                                        constrain( y_start +10 -i  , 0 , HEIGHT),col= c1)

                    elif block == BOOST_U:
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_col_lighted  , filled=True)
                        for i in range(1,6,3):
                            disp.line(constrain( x_start +7  -i  , 0 , WIDTH),
                                        constrain( y_start +3  +i  , 0 , HEIGHT),
                                        constrain( x_start +7  +i  , 0 , WIDTH),
                                        constrain( y_start +3  +i  , 0 , HEIGHT),col= c2)
                                
                    elif block == BOOST_L:
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_col_lighted  , filled=True)
                        for i in range(1,6,3):
                            disp.line(constrain( x_start +3  +i  , 0 , WIDTH),
                                        constrain( y_start +7  -i  , 0 , HEIGHT),
                                        constrain( x_start +3  +i  , 0 , WIDTH),
                                        constrain( y_start +7  +i  , 0 , HEIGHT),col= c1)

                    elif block == BOOST_R:
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_col_lighted  , filled=True)
                        for i in range(1,6,3):
                            disp.line(constrain( x_start +11 -i  , 0 , WIDTH),
                                        constrain( y_start +7  -i  , 0 , HEIGHT),
                                        constrain( x_start +11 -i  , 0 , WIDTH),
                                        constrain( y_start +7  +i  , 0 , HEIGHT),col= c2)
                                    
                    #.................................................................... START
                                    
                                
                    elif block == START:
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_start_lighted , filled=True)
                            
                    #.................................................................... GOAL
                                
                    elif block == GOAL:
                          
                        disp.rect(x_start_c,y_start_c,x_end_c,y_end_c,col = pf_start_lighted, filled=True)
                        disp.rect(constrain(x_start+1,0,WIDTH+1),constrain(y_start+1,0,HEIGHT+1),constrain(x_end-1,0,WIDTH+1),constrain(y_end-1,0,HEIGHT+1),col = c1, filled=False)
                        disp.rect(constrain(x_start+3,0,WIDTH+1),constrain(y_start+3,0,HEIGHT+1),constrain(x_end-3,0,WIDTH+1),constrain(y_end-3,0,HEIGHT+1),col = c2, filled=False)

                    #.................................................................... OSC STAGE
                                
                    elif block >= OSC_ST1 and block <= OSC_ST4:

                                
                        block_i = block - OSC_ST1


                        block_osc = abs((((loop_count//15)+block_i)%4)-2)
                                
                        if block_osc>=1:
                            osc_col = (constrain(pf_col_lighted[0]-(2-block_osc)*40,0,255),
                                        constrain(pf_col_lighted[1]-(2-block_osc)*40,0,255),
                                        constrain(pf_col_lighted[2]-(2-block_osc)*40,0,255))
                            disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = osc_col  , filled=True)

                            osc_start_x = constrain(x_start+2,0,WIDTH)
                            osc_start_y = constrain(y_start+2,0,HEIGHT)
                            osc_end_x   = constrain(x_end-2,0,WIDTH)
                            osc_end_y   = constrain(y_end-2,0,HEIGHT)

                    #.................................................................... ICE
                            
                    elif block == ICE:
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_start_lighted  , filled=False)

                    #.................................................................... DROPPER
                            
                    elif block >= DROPPER and block<=DROPPER+6:

                        drop = block-DROPPER # 0...4
                                
                        d_x_start = constrain(x_start + drop ,0,WIDTH)
                        d_y_start = constrain(y_start + drop ,0,HEIGHT)
                        d_x_end   = constrain(x_end   - drop ,0,WIDTH)
                        d_y_end   = constrain(y_end   - drop ,0,HEIGHT)

                        d_col = (constrain(pf_col_lighted[0]-drop*10,0,255),
                                    constrain(pf_col_lighted[1]-drop*10,0,255),
                                    constrain(pf_col_lighted[2]-drop*10,0,255))
                                                      
                        disp.rect(d_x_start , d_y_start , d_x_end , d_y_end , col = d_col  , filled=True)
                                
                        if block >= DROPPER+1 and (player_block_x!=x_array or player_block_y!=y_array):
                            playfield[x_array][y_array]+=1
                        
                    #.................................................................... TELEPORTER

                    elif (block>=65 and block<=67) or (block>=97 and block<=99):         # teleporter: ABC,abc
                                
                        disp.rect(x_start_c , y_start_c , x_end_c , y_end_c , col = pf_start_lighted , filled=False)
                        disp.rect(constrain(x_start_c +3,0, WIDTH),
                                    constrain(y_start_c +3,0, HEIGHT),
                                    constrain(x_end_c -3,0,   WIDTH),
                                    constrain(y_end_c -3,0,   HEIGHT), col = pf_start_lighted , filled=False)

                        teleporters[teleporters_visible][0] = x_start+BLOCK_SIZE//2 # remember positions for foreground drawing
                        teleporters[teleporters_visible][1] = y_start+BLOCK_SIZE//2
                        teleporters_visible+=1;


        #.................................... draw player foreground
        if player_on_blocks==True:
            draw_player(x_player_screen,y_player_screen,falling_gfx,warning,clamp_count)


                    
        #...................................................................................................
        #.................................................................  D R A W   F O R E G R O U N D  .
        #...................................................................................................

        #.................................... teleporter
        if (teleporters_visible>0):
                    
            tele_osc   = (loop_count%20)
            tele_color = (  constrain(pf_start_lighted[0] + tele_osc*3 ,0,255),
                            constrain(pf_start_lighted[1] + tele_osc*3 ,0,255),
                            constrain(pf_start_lighted[2] + tele_osc*3 ,0,255))
                        

            for i in range(teleporters_visible):
                x_tele = teleporters[i][0]-1-int(gx/3)
                y_tele = teleporters[i][1]-1-int(gy/3)
                tele_size = BLOCK_SIZE // 2 + 6 - (20-tele_osc)//2

                disp.circ( constrain( x_tele ,0,WIDTH)
                            ,constrain( y_tele,0,HEIGHT)
                            ,rad = tele_size 
                            ,col = tele_color, filled=False)
                        

#-------------------------------------------------------------- load score
def load_score ():
    global score           
    global level           
    global lifes            
    global hiscore
    global hilevel
    global hilifes
    global totaltime
    
    try: # check if there's a score file
        with open('/apps/micromarble/score.txt') as stats:
            score           = int(stats.read(8))
            level           = int(stats.read(4))
            lifes           = int(stats.read(5))
            hiscore         = int(stats.read(8))
            hilifes         = int(stats.read(5))
            totaltime       = int(stats.read(10))
    except:
        save_score()
    return

#-------------------------------------------------------------- save score
def save_score ():

    with open('/apps/micromarble/score.txt','w+') as stats:
        stats.write('%08d'%score)
        stats.write('%04d'%level)
        stats.write('%05d'%lifes)
        stats.write('%08d'%hiscore)
        stats.write('%05d'%hilifes)
        stats.write('%010d'%totaltime)

    return

#-------------------------------------------------------------- show time out
def show_timeout(bx_pos, by_pos):

    warning='TIME'
    size  = 2 # size of pixels 3=9chars
    c_pos = bx_pos -14 - timeout_timer

    color= constrain((10-abs(timeout_timer-10))*40,0,255)
    
    for i in range(len(warning)):
        c = ord(warning[i])-54
        if c==-21: #'!'
            c=37
        for x in range(5): # show character
            for y in range(5):
                if (alphabet[c][y][x]==1):
                    x_start =  x    * size + c_pos - int(gx * .2)
                    y_start =  y    * size + by_pos -20 - int(gy * .2)
                    if x_start >=0 and x_start <=WIDTH and y_start>=0 and y_start<=HEIGHT:
                        disp.pixel(x_start, y_start, col=(color,color,color))
        c_pos += 7 * size # increment start position of next character / writing from left to right
        
    warning='OUT'
    size  = 2 # size of pixels 3=9chars
    c_pos = bx_pos - 28 + timeout_timer
    for i in range(len(warning)):
        c = ord(warning[i])-54
        if c==-21: #'!'
            c=37
        for x in range(5): # show character
            for y in range(5):
                if (alphabet[c][y][x]==1):
                    x_start =  x    * size + c_pos - int(gx * .2)
                    y_start =  y    * size + by_pos +14 - int(gy * .2)
                    if x_start >=0 and x_start <=WIDTH and y_start>=0 and y_start<=HEIGHT:
                        disp.pixel(x_start, y_start, col=(color,color,color))
        c_pos += 7 * size # increment start position of next character / writing from left to right


        
    return

#-------------------------------------------------------------- show bonus
def show_bonus(number, bx_pos, by_pos, bonus_counter):

    if bonus_counter==9:
        vibra.vibrate(15)
    
    size  = 2 # size of pixels 3=9chars
    digits=len(str(number))
    c_pos = bx_pos + (digits*6//2) + 3

    for i in range(digits):

        digit = number % 10
        number //= 10
        c = digit            # c is character index in alphabet list

        for x in range(5): # show number
            for y in range(5):
                if (alphabet[c][y][x]==1):
                    x_start =  x    * size + c_pos - int(gx * .2)
                    y_start =  y    * size + by_pos - 37 + (bonus_counter**2)//6- int(gy * .2)
                    if x_start >=0 and x_start <WIDTH and y_start>=0 and y_start<HEIGHT:
                        disp.pixel(x_start, y_start, col=(150+bonus_counter*10,150+bonus_counter*10,150+bonus_counter*10))
        c_pos -= 7 * size # increment start position of next character / writing from right to left


    for x in range(5): # show '+'
        for y in range(5):
            if (alphabet[10][y][x]==1):
                x_start =  x    * size + c_pos - int(gx * .2)
                y_start =  y    * size + by_pos - 37 + (bonus_counter**2)//6- int(gy * .2)
                if x_start >=0 and x_start <WIDTH and y_start>=0 and y_start<HEIGHT:
                    disp.pixel(x_start, y_start, col=(150+bonus_counter*10,150+bonus_counter*10,150+bonus_counter*10))

    return


#-------------------------------------------------------------- write text
def write_text(text,x_text,y_text,text_col, px_space , fat=False , top_bound=0, bottom_bound=HEIGHT, left_bound=0, right_bound=WIDTH):
    
    for i in range(len(text)):
        c = ord(text[i])
        if c>=65: # 'A-Z'
            c=c-54
        elif c>=48 and c<=57: #'0...9'
            c=c-48
        elif c==33: # '!'
            c=37
        elif c==58: # ':'
            c=38
        elif c==32: # ' '
            c=39
        elif c==46: # '.'
            c=40
        elif c==ord('-'): # '-'
            c=41
            
            
        for x in range(5): # show character
            for y in range(5):
                if (alphabet[c][y][x]==1):
                    x_start =  x * px_space + x_text 
                    y_start =  y * 2 + y_text
                    if x_start >=left_bound and x_start <=right_bound and y_start>=top_bound and y_start<=bottom_bound:
                        disp.pixel(x_start, y_start, col=text_col)
                        if fat==True:
                            disp.pixel(x_start+1, y_start, col=text_col)
        x_text += 7 * px_space # increment start position of next character  
    return
#-------------------------------------------------------------- draw one 3d line, rotated in x,y,z 
def draw_3d_line(x1,y1,z1,x2,y2,z2,l_color=(255,255,255),rx = 0,ry = 0,rz = 0,persp = 8, zoom=3 ):

    zoom = -zoom
    l_start = (x1,y1,z1)
    l_end = (x2,y2,z2)
    
    l_start = rot_z(l_start, rz)
    l_end   = rot_z(l_end  , rz)
    
    l_start = rot_x( l_start , rx)
    l_end   = rot_x( l_end   , rx)

    l_start = rot_y(l_start, ry)
    l_end   = rot_y(l_end  , ry)


    screen_s_x = int( ( l_start[0] * zoom * ( l_start[2] + persp ) ) + 80)
    screen_s_y = int( ( l_start[1] * zoom * ( l_start[2] + persp ) ) + 40)
    screen_e_x = int( ( l_end[0]   * zoom * ( l_end[2]   + persp ) ) + 80)
    screen_e_y = int( ( l_end[1]   * zoom * ( l_end[2]   + persp ) ) + 40)

    r_line( screen_s_x, screen_s_y, screen_e_x, screen_e_y, color=l_color)
        
    return


#-------------------------------------------------------------- draw 3d rectangle, rotated in x,y,z 
def draw_3d_square(size_x=1, size_y=1, x_pos=0, y_pos=0, z_pos=0, square_col=(255,255,255),rx = 0,ry = 0,rz = 0, persp = 8, zoom=3 ,   shade=False): #p5z4.5
    #size x, size y, pos x, pos y, pos z, color

    x_pos-=.5
    y_pos-=.5
    
    v_start  = ( (  x_pos      * size_x , y_pos * size_y , z_pos ) , (   x_pos       * size_x , ( 1 + y_pos ) * size_y , z_pos ) )
    v_end    = ( ( (1 + x_pos) * size_x , y_pos * size_y , z_pos ) , ( ( 1 + x_pos ) * size_x , ( 1 + y_pos ) * size_y , z_pos ) )

    h_start  = ( ( x_pos * size_x ,  y_pos        * size_y , z_pos) , ( ( 1 + x_pos ) * size_x ,   y_pos       * size_y , z_pos ) )
    h_end    = ( ( x_pos * size_x , ( 1 + y_pos ) * size_y , z_pos) , ( ( 1 + x_pos ) * size_x , ( 1 + y_pos ) * size_y , z_pos ) )

    zoom = -zoom

    #draw square which is rotated in x and y
    for p in range(2):

        #............................................. vertical lines
        v_s = rot_z( v_start[p] , rz)
        v_e = rot_z( v_end[p]   , rz)

        v_s = rot_x(v_s, rx)
        v_e = rot_x(v_e, rx)

        v_s = rot_y(v_s, ry)
        v_e = rot_y(v_e, ry)


        screen_v_s_x    = int( ( v_s[0] * zoom * ( v_s[2] + persp ) ) + 80)
        screen_v_s_y    = int( ( v_s[1] * zoom * ( v_s[2] + persp ) ) + 40)
        screen_v_e_x    = int( ( v_e[0] * zoom * ( v_e[2] + persp ) ) + 80)
        screen_v_e_y    = int( ( v_e[1] * zoom * ( v_e[2] + persp ) ) + 40)

        if shade==True:
            draw_color = ( ( constrain( int( square_col[0] + ( v_s[2] + v_e[2] ) * 20 ) , 0 , 255 )),
                           ( constrain( int( square_col[1] + ( v_s[2] + v_e[2] ) * 20 ) , 0 , 255 )),
                           ( constrain( int( square_col[2] + ( v_s[2] + v_e[2] ) * 20 ) , 0 , 255 )))
        else:
            draw_color = square_col

        r_line( screen_v_s_x , screen_v_s_y , screen_v_e_x , screen_v_e_y , color = draw_color)

        #............................................. horizontal lines
        h_s = rot_z( h_start[p] , rz)
        h_e = rot_z( h_end[p]   , rz)

        h_s = rot_x(h_s, rx)
        h_e = rot_x(h_e, rx)

        h_s = rot_y(h_s, ry)
        h_e = rot_y(h_e, ry)

        screen_h_s_x =  int( ( h_s[0] * zoom * ( h_s[2] + persp ) ) + 80)
        screen_h_s_y =  int( ( h_s[1] * zoom * ( h_s[2] + persp ) ) + 40)
        screen_h_e_x    =  int( ( h_e[0] * zoom * ( h_e[2] + persp ) ) + 80)
        screen_h_e_y    =  int( ( h_e[1] * zoom * ( h_e[2] + persp ) ) + 40)
        
        if shade==True:
            draw_color = ( ( constrain( int( square_col[0] + ( h_s[2] + h_e[2] ) * 30 ) , 0 , 255 ) ) ,
                           ( constrain( int( square_col[1] + ( h_s[2] + h_e[2] ) * 30 ) , 0 , 255 ) ) ,
                           ( constrain( int( square_col[2] + ( h_s[2] + h_e[2] ) * 30 ) , 0 , 255 ) ) )
        else:
            draw_color = square_col

        r_line( screen_h_s_x , screen_h_s_y , screen_h_e_x , screen_h_e_y , color=draw_color)
        
    return




#-------------------------------------------------------------- robust line (coordinates can be floats and outside screen - line will still be displayed)
def r_line(x1,y1,x2,y2,color=(255,255,255)):

    if x1>=0 and x1<= WIDTH and  x2>=0 and x2<= WIDTH and y1>=0 and y1<= HEIGHT and y2>=0 and y2<= HEIGHT: # line is on screen
        disp.line(int(x1),int(y1),int(x2),int(y2),col=color)
    else:
        
        if x1>WIDTH:
            dxa = x1  - x2     # x-distance
            dxb = WIDTH - x2

            r=1
            if dxa>0:
                r   = dxb / dxa
            
            dy  = y1 - y2      # y-distance
            dy  = dy * r
            y1  = y2 + dy
            x1  = WIDTH

        elif x1<0:
            dxa = x2  - x1     # x-distance
            dxb = 0 - x2
            r=1
            if dxa>0:
                r   = dxb / dxa
            dy  = y2 - y1      # y-distance
            dy  = dy * r
            y1  = y2 + dy
            x1  = 0

        if x2>WIDTH:
            dxa = x2  - x1    # x-distance
            dxb = WIDTH - x1
            r=1
            if dxa>0:
                r   = dxb / dxa
            dy  = y2 - y1      # y-distance
            dy  = dy * r
            y2  = y1 + dy
            x2  = WIDTH

        elif x2<0:
            dxa = x1  - x2     # x-distance
            dxb = 0 - x1
            r=1
            if dxa>0:
                r   = dxb / dxa
            dy  = y1 - y2      # y-distance
            dy  = dy * r
            y2  = y1 + dy
            x2  = 0
            
        if y1>HEIGHT:
            dya = y1  - y2     # y-distance
            dyb = HEIGHT - y2
            r=1
            if dya>0:
                r   = dyb / dya
            dx  = x1 - x2      # x-distance
            dx  = dx * r
            x1  = x2 + dx
            y1  = HEIGHT

        elif y1<0:
            dya = y2  - y1     # y-distance 
            dyb = 0 - y2
            r=1
            if dya>0:
                r   = dyb / dya
            dx  = x2 - x1      # x-distance 
            dx  = dx * r
            x1  = x2 + dx
            y1  = 0

        if y2>HEIGHT:
            dya = y2  - y1    # y-distance
            dyb = HEIGHT - y1
            r=1
            if dya>0:
                r   = dyb / dya
            dx  = x2 - x1      # x-distance
            dx  = dx * r
            x2  = x1 + dx
            y2  = HEIGHT

        elif y2<0:
            dya = y1  - y2     # y-distance
            dyb = 0 - y1
            r=1
            if dya>0:
                r   = dyb / dya
            dx  = x1 - x2      # x-distance
            dx  = dx * r
            x2  = x1 + dx
            y2  = 0
        
        disp.line(constrain(int(x1),0,WIDTH),constrain(int(y1),0,HEIGHT),constrain(int(x2),0,WIDTH),constrain(int(y2),0,HEIGHT),col=color)

    return

#-------------------------------------------------------------- calibrate
def show_calib_screen():
    global calib_x
    global calib_y
    global calib_z
    global game
    global calib_count
    global calib_return_count
    global p_initial_gx
    global p_initial_gy
    global p_initial_gz
    global initial_gx
    global initial_gy
    global initial_gz
    global start_screen_b_speed
    global start_screen_b_pos  
    global start_screen_b_p_pos
    global calib_return_ax
    global calib_return_ay
    global calib_return_az
    global scroll_text 
    

    #.............................................. init
    if calib_count == 0:
        print('calib init')
        scroll_text = 0
    
        calib_return_count = 0
        calib_return_ax = (gx%360)/180*3.1415
        calib_return_ay = (gy%360)/180*3.1415
        calib_return_az = (gz%90)/180*3.1415

        calib_count = 1
        calib_x = 0
        calib_y = 0
        calib_z = 0
        p_initial_gx = 0
        p_initial_gy = 0
        p_initial_gz = 0
        initial_gx = 0
        initial_gy = 0
        initial_gz = 0
        start_screen_b_speed = -35
        start_screen_b_pos   = 45
        start_screen_b_p_pos = 45




    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ calibration 3d view 
    else:
        calib_count += 1
        
        trans_in = constrain(40 - calib_count,0,40)
        
        trans_fg = ((calib_return_count+ trans_in) ** 2  ) / 6 # foreground transition counter (overlay) 0...
        trans_bg = ( calib_return_count+ trans_in) * .75       # background transition counter (cube)    0 ...


        p_initial_gx = initial_gx
        p_initial_gy = initial_gy
        p_initial_gz = initial_gz
        
        if calib_return_count == 0:
            initial_gx = (gx%360)/180*3.1415
            initial_gy = (gy%360)/180*3.1415
            initial_gz = (initial_gz + ((gz%90)-(p_gz%90))/180*3.1415)*.92

        
        rx = initial_gx
        ry = initial_gy
        rz = initial_gz




        #.......................................... draw scale 

        for mx in range(30,WIDTH-9,20):
            r_line(mx, 15-trans_fg, mx, 19-trans_fg, (0,100,25))                # | l
            r_line(mx, 60+trans_fg, mx, 64+trans_fg, (0,100,25))                # | r
        
        r_line(80,15-trans_fg,80,24-trans_fg,(0,100,25))                        # | t
        r_line(80,37+trans_fg,80,43+trans_fg,(0,100,25))                        # | b
        if trans_fg==0:
            r_line(80,55,80,64,(0,100,25))                                      # | c
            
        for my in range(20,65,10):
            r_line(10-trans_fg, my, 14-trans_fg, my,(0,100,25))                 # - l
            r_line(WIDTH-10+trans_fg, my, WIDTH-14+trans_fg, my,(0,100,25))     # - r
            
        
        r_line(21-trans_fg, 40, 30-trans_fg, 40,(0,150,50))                     # - l
        r_line(WIDTH-21+trans_fg, 40, WIDTH-30+trans_fg, 40,(0,100,25))         # - r
        if trans_fg==0:
            r_line(77, 40, 83, 40,(0,100,25))                                   # - c

        for ri in range(5,20 + constrain(trans_fg*2 ,0,10),10):                                               # dashed circle segments
            ric1=rot_z((45+trans_fg/2,-2- trans_fg/20 ,0),ri/40)
            ric2=rot_z((45+trans_fg/2, 2+ trans_fg/20 ,0),ri/40)
            r_line(80 + ric1[0], 40 + ric1[1], 80 + ric2[0], 40 + ric2[1],(0,100,25))
            r_line(80 - ric1[0], 40 + ric1[1], 80 - ric2[0], 40 + ric2[1],(0,100,25))
            r_line(80 + ric1[0], 40 - ric1[1], 80 + ric2[0], 40 - ric2[1],(0,100,25))
            r_line(80 - ric1[0], 40 - ric1[1], 80 - ric2[0], 40 - ric2[1],(0,100,25))

        xs = constrain( int(10-trans_fg)       , 0, WIDTH)    # rectangle
        xe = constrain( int(WIDTH-10+trans_fg) , 0, WIDTH)
        ys = constrain( int(15-trans_fg)       , 0, HEIGHT)
        ye = constrain( int(64+trans_fg)       , 0, HEIGHT)
        disp.rect(xs,ys,xe,ye,col=(0,150,50),filled=False)

            

        #.......................................... draw cube
        for i in range(-1,2):
            sc  = constrain(int((i*30+160)),0,255)
            div = constrain(trans_bg/26,0,1) 
            draw_3d_square(size_x=1.5+trans_bg/20, size_y=1.5+trans_bg/40,
                           x_pos=0, y_pos=0, z_pos=i*.75+trans_bg/20,
                           square_col=(sc,sc,sc),
                           rx = rx*(1-div) + calib_return_ax*div,
                           ry = ry*(1-div) + calib_return_ay*div,
                           rz = rz*(1-div) + calib_return_az*div,
                           persp = 3-trans_bg/2, zoom=8+trans_bg/12, shade=True
                           )
            
            
        #...................................................... moving markers
        
        mark_x = (int(gx+540)%360) - 180                      # -180...180
        mx=WIDTH//2-mark_x*140//360                           # centered at screen overlay
        
        mark_y = (int(gy+540)%360) - 180                      # -180...180
        my=HEIGHT//2 - mark_y * 50//360                       # centered at screen overlay

        r_line(mx,14-trans_fg/2,mx,21-trans_fg,(0,250,150))                 # | marker x
        r_line(mx,58+trans_fg/2,mx,65+trans_fg,(0,250,150))                 # |

        r_line(9-trans_fg, my, 18-trans_fg/2, my,(0,250,150))               # - marker y
        r_line(WIDTH-9+trans_fg, my, WIDTH-18+trans_fg/2, my,(0,250,150))   # -

        if trans_fg==0:
            r_line(mx-2,my,mx+2,my,(0,250,150))              # +:-
            r_line(mx,my-2,mx,my+2,(0,250,150))              # +:|

        rzc  = rx                                            # rotary marker
        rzc1 = rot_z((38+trans_fg/4,0,0),rzc)
        rzc2 = rot_z((47+trans_fg/3,0,0),rzc)
        r_line(80 + rzc1[0] , 40 + rzc1[1] , 80 + rzc2[0] , 40 + rzc2[1],(0,250,150))
        r_line(80 - rzc1[0] , 40 - rzc1[1] , 80 - rzc2[0] , 40 - rzc2[1],(0,250,150))

        #......................................................  black rectangle mask during transition
        bl_mask=constrain((trans_bg - 16)**2/2,0,100)
        if bl_mask>0:
            r_line( 80-bl_mask, 0,          80+bl_mask,  0,         (0,0,0) )            
            r_line( 80-bl_mask, HEIGHT,     80+bl_mask,  HEIGHT,    (0,0,0) )
            r_line( WIDTH,      40-bl_mask, WIDTH,       40+bl_mask,(0,0,0) )
            r_line( 0,          40-bl_mask, 0,           40+bl_mask,(0,0,0) )


        
        #...................................................... text overlay
            
        write_text('CALIBRATE' , 20 , int(2-trans_fg * 1.33)  , (0,250,150),2, False )
        write_text('OK' , 17   , int( 69 + trans_fg  * 1.33 )  , (0,250,150), 2, True )
        

        if trans_fg == 0:
            #.................................................. ok arrow
            for i in range(0,6,2):
                r_line(10-i  ,68+i,10-i  ,78-i,(0,100+((calib_count-i)//4)%5*30,30+(((calib_count-i)//4)%5)*30))
                
        #.......................................... vibrate
        if trans_fg > 0 :
            vibra.vibrate(5)

            
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ calibration is set - transit to start screen
    if calib_return_count > 0:        
        calib_return_count += 1
        
    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ end of transit return to screen where user came from
    if calib_return_count == 40:
        
        vibra.vibrate(8)
        calib_return_count = 0
        calib_count = 0
        game = calib_return
        disp.clear()
        
    #.......................................................... if key is pressed: remember calibration values and prepare transition    

    pressed = buttons.read(buttons.BOTTOM_LEFT)

    if ( pressed & buttons.BOTTOM_LEFT != 0 ) and calib_return_count == 0 and p_pressed==0:
        
        calib_return_count = 1
        
        calib_x    = gx
        calib_y    = gy
        calib_z    = gz

        if rx>3.1415:
            calib_return_ax = 6.28
        else:
            calib_return_ax = 0
            
        if ry>3.1415:
            calib_return_ay = 6.28
        else:
            calib_return_ay = 0


        print('calibration values: x:'+str(gx)+' y:'+str(gy)+' z:'+str(gz))
    
    return
        


        




#-------------------------------------------------------------- show start screen
def show_start_screen():
    global start_screen_count
    global game
    global level
    global score
    global lifes
    global loop_count
    global start_screen_b_pos
    global start_screen_b_speed
    global start_screen_b_p_pos 
    
    start_screen_count+=1


            
    #.................................... draw background
    
    for x in range( (int(-gx/3)+100)%12 , WIDTH , 12):
        disp.line(x,0,x,HEIGHT,col=(40,40,40))
    for y in range((int(-gy/3)+100)%12 , HEIGHT , 12):
        disp.line(0,y,WIDTH,y,col=(40,40,40))
        
    #.................................... microball
    start_screen_b_p_pos = start_screen_b_pos 
    start_screen_b_speed = (start_screen_b_speed*2 + gx)/3
    start_screen_b_pos = start_screen_b_pos + start_screen_b_speed

    v_time=0
    
    if start_screen_b_pos<0:
        start_screen_b_pos=0
        start_screen_b_speed=abs(start_screen_b_speed*.85)
        v_time=int(abs(start_screen_b_speed))

    if start_screen_b_pos>46:
        start_screen_b_pos=46
        start_screen_b_speed=-abs(start_screen_b_speed*.85)
        v_time=int(abs(start_screen_b_speed))
                   
    if v_time>2 and start_screen_b_p_pos != start_screen_b_pos :
        
        vibra.vibrate(constrain(v_time+3,3,8))
        
    draw_player(102+int(start_screen_b_pos),17,-1)

    #.......................................................... title gfx
    xs = constrain(int (- gx /12 ),-15, 15 )
    for y in range(11): # show title tile
            for x in range(30):
                if (title[y][x]>0):

                    tile_color=title_colors[title[y][x]-1]
                    
                    x_start =  x * 4 + 21
                    y_start =  y * 4 + 8 
                    
                    if x_start >=0 and x_start <=WIDTH-3 and y_start>=0 and y_start<=HEIGHT-3:
                        disp.rect(x_start, y_start, x_start+2, y_start+2,col=tile_color, filled=True)
        

        
    #.......................................................... draw menu

    #.......................................................... play arrow
    xs = 0 # xshift
    disp.rect(85 + xs ,54,148 + xs ,64,col=(85,85,85),filled=True) 
    for i in range(6):
        r_line( 149 + i + xs, 54 + i, 149 + i + xs, 64 - i , (110,110,110) )
    write_text('PLAY' , 92 + xs  , 55  , (255,255,255), 2,True )
    #.......................................................... new arrow
    if (score!=0 or lifes!=1):
        disp.rect(98 + xs ,68,148 + xs,78,col=(60,60,60),filled=True) 
        for i in range(6):
            r_line(149+i + xs,68+i,149+i + xs,78-i,(85,85,85))
        write_text('NEW' , 106 + xs  , 69  , (255,255,255), 2 )
    #.......................................................... calib arrow

    disp.rect(11 + xs ,68,94 + xs ,78,col=(60,60,60),filled=True) 
    for i in range(6):
        r_line(10-i + xs ,68+i,10-i + xs ,78-i,(85,85,85))
    write_text('CALIB.' , 17 + xs  , 69 , (255,255,255), 2 )


        
    return


#-------------------------------------------------------------- write fx text
def fx_text(text,x_text,y_text,text_col, top_bound=0, bottom_bound=HEIGHT, left_bound=0, right_bound=WIDTH):
    
    for i in range(len(text)):
        c = ord(text[i])
        if c>=65: # 'A-Z'
            c=c-54
        elif c>=48 and c<=57: #'0...9'
            c=c-48
        elif c==33: # '!'
            c=37
        elif c==58: # ':'
            c=38
        elif c==32: # ' '
            c=39
        elif c==46: # '.'
            c=40
        elif c==ord('-'): # '-'
            c=41
        elif c==ord('/'): # '/'
            c=42


            
            
        for x in range(5): # show character
            for y in range(5):
                if (alphabet[c][y][x]==1):
                    x_start =  x * 3 + x_text
                    y_start =  y * 3 + y_text

                    #y_start += int(math.sin(x_start/12 - pause_count/5)*3)
                                        
                    if x_start >=left_bound and x_start <=right_bound and y_start>=top_bound and y_start<=bottom_bound:
                        disp.rect(x_start, y_start,x_start+1,y_start+1, col=text_col, filled=True)
        x_text += 7 * 3 # increment start position of next character  
    return

#-------------------------------------------------------------- draw scroll text line
def write_scroll_text(text_line, text_color):
    global scroll_y
    if scroll_y>1 and scroll_y<52:
        fx_text(text_line, WIDTH//2 - (len(text_line) * 21) //2 + 3 , scroll_y , text_color  ,top_bound=15 , bottom_bound=49 )
    scroll_y += 20
    return

#-------------------------------------------------------------- draw dashed line (only horizontal and vertical from small coordinate value to large value)
def dashed_line(sx,sy,ex,ey, distance, offset, color):
    offset = offset%(distance*2)
    if sx == ex:
        for y in range(sy+offset-distance,ey,distance*2):
            disp.line(sx,constrain(y,sy,ey),sx,constrain(y+distance,sy,ey),col=color)
    if sy == ey:
        for x in range(sx+offset-distance,ex,distance*2):
            disp.line(constrain(x,sx,ex),sy,constrain(x+distance,sx,ex),sy,col=color)
    return

#-------------------------------------------------------------- show pause screen
def show_pause_screen():
    
    global start_screen_count
    global game
    global level
    global score
    global lifes
    global loop_count
    global pause_count
    global scroll_text
    global scroll_y
    
    pause_count+=1
    if pause_count==1:
        disp.clear()

    disp.rect(0, 0, WIDTH, 50, col=(0,0,0),     filled=True)
    #--------------------------- headline and frame
    write_text('GAME PAUSED +++', (333 - pause_count)% 444 - WIDTH - 80, 2  , start_color, 2, True )
    write_text('GAME PAUSED +++', (111 - pause_count)% 444 - WIDTH - 80, 2  , start_color, 2, True )
        
    #.......................................................... scroll text

    text_lines = 12
    
    scroll_text += 1
    if scroll_text > text_lines * 20 + 16:
        scroll_text = 0
    scroll_y = 52 - int(scroll_text)

    write_scroll_text('STAGE'        , (200,200,200)   )
    write_scroll_text(str(level)     , (200,200,200)   )
    write_scroll_text('///'          , start_color )
    write_scroll_text('MARBLES'      , (200,200,200)   )
    write_scroll_text(str(lifes)     , (200,200,200)   )
    write_scroll_text('///'          , start_color )
    write_scroll_text('SCORE'        , (200,200,200)   )
    write_scroll_text(str(score)     , (200,200,200)   )
    write_scroll_text('///'          , start_color )
    write_scroll_text('HISCORE'      , (200,200,200)   )
    write_scroll_text(str(hiscore)   , (200,200,200)   )
    
    disp.rect(0, 15, WIDTH, 50, col=start_color, filled=False)
    dashed_line( 0 ,     15 , WIDTH , 15 , 5  , pause_count   , playfield_color )
    dashed_line( WIDTH , 15 , WIDTH , 50 , 5  , pause_count   , playfield_color )
    dashed_line( 0 , 50 , WIDTH     , 50 , 5  , -pause_count  , playfield_color )
    dashed_line( 0 ,     15 , 0     , 50 , 5  , -pause_count , playfield_color )

    
        

    #.......................................................... draw menu
    
    if pause_count==1:
        scroll_text=0
        #--------------------------- play arrowxs = 0 # xshift
        for i in range(6):
            r_line( 154 + i , 54 + i, 154 + i , 64 - i , start_color )
        write_text('RESUME' , 68   , 55  , (255,255,255), 2,True )
        
        #--------------------------- new arrow
        if (score!=0 or lifes!=1):
            for i in range(6):
                r_line(154+i ,68+i,154+i ,78-i, start_color )
            write_text('NEW' , 111 , 69  ,  start_color , 2, True )
        
        #--------------------------- calib arrow
        for i in range(6):
            r_line(5-i ,68+i,5-i ,78-i, start_color )
        write_text('CALIB.' , 12 , 69 ,  start_color , 2 , True)
        
    return 


#-------------------------------------------------------------- load playfield 

def load_playfield(level):
    global playfield
    global x_array_max
    global y_array_max
    global player_origin_x
    global player_origin_y
    x_array_max = 0
    y_array_max = 0

    global playfield_color
    global start_color
    global playfield_bgnd
    global max_time
    global next_level
    next_level=False
    clamp_count = 10 # ball clamp counter

    global tele_block_x                           # block coordinates of teleporters in playfield
    global tele_block_y
    global teleporting

    global player_x    
    global player_y      
    global new_player_x 
    global new_player_y 
    

    print('.............................')
    print(playfield)
    print('.............................')
    print('loading file')
    with open('/apps/micromarble/'+str(level)+'.txt') as field:
        #........................................ header
        field.read(2)
        max_time        = int(field.read(4))
        field.read(3)
        red             = int(field.read(3))
        field.read(3)
        green           = int(field.read(3))
        field.read(3)
        blue            = int(field.read(3))
        field.read(6)
        playfield_bgnd  = int(field.read(1))
        field.read(2)
        playfield_color = (red,green,blue)
        start_color     = (constrain(red+50,0,255),constrain(green+50,0,255),constrain(blue+50,0,255))
        
        #........................................ load map into array
        #                                         (here happens a swap between x- and y- axis. It's a bug, but it doesn't really bug me ;)

        x=0
        read_line=True
        while read_line==True:
            playfield.append([])
            read_char=True
            y=0
            while read_char==True:
                tile=field.read(1)
                tile_num=ord(tile)
                if(tile_num==EOF):                      #found 'e'? (== end of file)
                    read_char=False
                    read_line=False
                    
                if(tile_num==START):                    #found starting point?
                    player_origin_x = x * BLOCK_SIZE + BLOCK_SIZE // 2
                    player_origin_y = y * BLOCK_SIZE + BLOCK_SIZE // 2


                if ((tile_num>=65 and tile_num<=67) or (tile_num>=97 and tile_num<=99)): # found teleporter?! (A,B,C,a,b,c)
                    tele_index = tile_num
                    if tele_index >=97:
                        tele_index -= 94
                    else:
                        tele_index -= 65
                    tele_block_x [tele_index] = x  # 0:A, 1:B, 2:C, 3:a, 4:b, 5:c
                    tele_block_y [tele_index] = y
                    
                     
                if(tile_num!=13 and read_char==True):   # no new line
                    playfield[x].append(tile_num)       # add value to array
                    y+=1
                    if y>y_array_max:
                        y_array_max=y
                else:
                    read_char=False
            if read_line==True:
                field.read(1)
                x+=1
        x_array_max=x
        
    #print('.............................')
    #print(playfield)
    #print('.............................')
    #print('xmax:%i ymax:%i'%(x_array_max,y_array_max))
    #print('.............................')

    
    try: # check if there's a next level
        with open('/apps/micromarble/'+str(level+1)+'.txt') as field:
            next_level=True
            print('Next level found!')
    except:
        next_level=False
        print('This was the final level!')


    player_x = player_origin_x 
    player_y = player_origin_y 
    new_player_x  = player_origin_x 
    new_player_y  = player_origin_y 

    
    return

#-------------------------------------------------------------- reset playfield
def reset_playfield():
    global playfield
    global x_array_max
    global y_array_max
    
    for x in range(x_array_max):
        playfield[x]=[]
    playfield=[]
    x_array_max=0
    y_array_max=0

    print('.......destroyed:..........')
    print(playfield)
    print('.............................')
    print('xmax:%i ymax:%i'%(x_array_max,y_array_max))
    print('.............................')
    return



#-------------------------------------------------------------- constrain
def constrain(value, minimum, maximum):
    if value<minimum:
        value=minimum
    if value>maximum:
        value=maximum
    return value

#-------------------------------------------------------------- rotatation matrices

def rot_x(p=(1,1,1),a=0):
    x =  math.cos(a) * p[0] + math.sin(a) * p[2]
    y =  p[1]
    z = -math.sin(a) * p[0] + math.cos(a) * p[2]
    return (x,y,z)

def rot_y(p=(1,1,1),a=0):
    x =  p[0]
    y =  math.cos(a) * p[1] + math.sin(a) * p[2]
    z = -math.sin(a) * p[1] + math.cos(a) * p[2]
    return (x,y,z)

def rot_z(p=(1,1,1),a=0):
    x =  math.cos(a) * p[0] + math.sin(a) * p[1]
    y = -math.sin(a) * p[0] + math.cos(a) * p[1]
    z =  p[2]
    return (x,y,z)


#-------------------------------------------------------------- read gyro
def read_gyro():
    return_value=False
    samples = sensors[sensor]["sensor"].read()
    if len(samples) > 0:                    #get orientation sensor samples
        sample = samples[0]
        global gx
        global gy
        global gz

        global p_gx
        global p_gy
        global p_gz

        p_gx = gx
        p_gy = gy
        p_gz = gz
        
        gx=((-sample.z-calib_x)+3600)%720-360
        gy=((-sample.y-calib_y)+3600)%720-360
        gz=(( sample.x-calib_z)+3600)%720-360
        return_value=True
    return return_value

#-------------------------------------------------------------- draw crosshair
def draw_crosshair(x_pos=0,y_pos=0):
    x=int(constrain(gx/2+10,0,20))
    y=int(constrain(gy/2+10,0,20))

    disp.rect(x_pos, y_pos, x_pos+20, y_pos+20, col=(64,64,64))
    disp.line(x_pos+x, y_pos, x+x_pos,  y_pos+20, col=(255,0,0))
    disp.line(x_pos, y_pos+y, x_pos+20, y_pos+y,  col=(255,0,0))
    return
        
#-------------------------------------------------------------- draw player                    
def draw_player(x_pos,y_pos,p_size,warning=0,clamp_count=0):
        
        
    hlx=constrain(-int(gx),-1,1)
    hly=constrain(int(gy),-1,1)
    ll=int(light_level/2-falling*4-warning)

    fill_val = teleporting==0

    if teleporting != p_teleporting and (teleporting == 0 or p_teleporting ==0):
        p_size-=1
        vibra.vibrate(320)

    if teleporting==0:
        #...................................................... normal player rendering
        if(extra_time_counter==5):
            p_size-=1
            #for led in range(11,15):
                #leds.prep(led, (140,140,140))
    
        if p_size<6:
            disp.circ(constrain(x_pos-2+hlx,0,WIDTH+1),constrain(y_pos+2+hly,0,HEIGHT+1),rad=8-p_size, col = (0,0,0), filled=True)#shadow
            cl = constrain(190 + ll,0,255)
            disp.circ(x_pos,y_pos,rad=8-p_size, col = (cl,cl,cl), filled=True)
            cl = constrain(120 + ll,0,255)
            disp.circ(x_pos+hlx,y_pos+hly,rad=6-p_size, col = (cl,cl,cl), filled=True)
            cl = constrain(200 + ll,0,255)
            disp.circ(x_pos+1,y_pos-1,rad=6-p_size, col = (cl,cl,cl), filled=True)
        if p_size<2:
            cl = constrain(255 + ll,0,255)
            disp.circ(x_pos+1-hlx-p_size//4,y_pos-3-hly-p_size,rad=2, col = (cl,cl,cl), filled=True) # highlight


        #...................................................... clamp        
        if clamp_count>0:
            cl = constrain(abs(clamp_count*30-150),0,160)
            disp.circ(x_pos,y_pos,rad=10+clamp_count//3, col = (cl,cl,cl), filled=False)
            cl = 200-cl
            disp.circ(x_pos,y_pos,rad=11+clamp_count, col = (cl,cl,cl), filled=False)
            
        if clamp_count==1:
            vibra.vibrate(9)
        if clamp_count==9:
            vibra.vibrate(12)
            

    else:
        #...................................................... ghost player rendering [=[[= teleportation =]]=]
        
        tele_osc = abs((teleporting%14)-7) # 0...7
        if(game==RUNNING):
            vibra.vibrate(tele_osc+5)
        cl = constrain(190 + ll,0,255)
        


        disp.circ(x_pos,y_pos,rad=7-p_size, col = (cl,cl,cl), filled=False)
        disp.circ(x_pos+3+hlx-p_size//4,y_pos-3-hly-p_size//4,rad=1, col = (cl,cl,cl), filled=True) # highlight
        cl = constrain(80+(20*(tele_osc)) + ll,0,255)

        
        ring_color = (  constrain(pf_start_lighted[0] + tele_osc*20 ,0,255),
                        constrain(pf_start_lighted[1] + tele_osc*20 ,0,255),
                        constrain(pf_start_lighted[2] + tele_osc*20 ,0,255))

                
        disp.circ(x_pos,y_pos,rad=12, col = ring_color, filled=False)

        led_color = (  constrain(pf_start_lighted[0] +(tele_osc*20) -70,0,255),
                       constrain(pf_start_lighted[1] +(tele_osc*20) -70,0,255),
                       constrain(pf_start_lighted[2] +(tele_osc*20) -70,0,255))
                
        for led in range(11,15):
            leds.prep(led, led_color)
        
        

    return

#-------------------------------------------------------------- main
with display.open() as disp:


    
    pf_orig_x = 0  # origin of playfield (top,left) relative to screen origin (top,left)
    pf_orig_y = 0  # origin of playfield relative to screen origin    pf_width  = x_array_max * BLOCK_SIZE # playfield width in pixels
    pf_height = y_array_max * BLOCK_SIZE # playfield height in pixels
    player_block_x = 0 # block coordinates of player
    player_block_y = 0
    p_player_block_x = 0 # previous block coordinates of player
    p_player_block_y = 0
    player_x_speed = 0.0 # speed of player
    player_y_speed = 0.0
    loop_count=0
    falling = 0
    winning = 0
    player_on_blocks = True
    led_timer=11
    p_led_timer=11
    next_level = False
    winning_time = 0
    show_bonus_timer = 0
    bonus = 0
    score = 0
    warning = 0
    lifes = 1
    light_level=0
    teleporting  = False
    tele_block_below = False # remembers if the block below player is a teleporter or not
    block_below = 32   # block below player
    p_block_below = 32 # block below player in previous loop
    tele_distance = 0  # distance to next teleporter
    pf_col_lighted   = (0,0,0)
    pf_start_lighted = (0,0,0)

    load_score ()
    

    #============================================================================================= Let's go! ============
    
    while True:
        
	#---------------------------------------- read orientation sensor
    	
        if read_gyro(): #updates gx,gy,gz



            

            #.................................................................................................                     
            #............................................................................. STATE MACHINE .....
            #.................................................................................................

            #---------------------------------------- read brightness sensor and adapt display & led brightness
            if game!=p_game:
                
                bri=light_sensor.get_reading()*4

                disp.backlight(constrain(bri,50,100))

                led_bri=(bri)/100*8      

                leds.dim_top(constrain(int(led_bri),4,8))
                leds.dim_bottom(constrain(int(led_bri+1),4,8))
            
            p_game = game
            
            #.................................... update all buttons
            
     
            p_pressed = pressed
            
            pressed   = buttons.read(buttons.TOP_RIGHT | buttons.BOTTOM_RIGHT  | buttons.BOTTOM_LEFT )
            
            button_top_right    = pressed & buttons.TOP_RIGHT    != 0 
            button_bottom_right = pressed & buttons.BOTTOM_RIGHT != 0
            button_bottom_left  = pressed & buttons.BOTTOM_LEFT  != 0

            if pressed!=0 and p_pressed==0 and game!=CALIB:
                vibra.vibrate(15)
                

            #.................................... calib                  
            if game == CALIB:
                disp.clear()
                show_calib_screen()          
                
            #....................................  start screen
            if game == START_SCREEN:
                light_level=0
                disp.clear()
                show_start_screen()

                #----- check buttons -----
                if p_pressed == 0 and button_bottom_left  == True: # calibrate
                    print('calib!')
                    game = CALIB
                    disp.clear()
                    
                if p_pressed == 0 and button_top_right    == True: # play
                    p_pressed = pressed
                    print('play!')
                    loop_count = 0
                    light_level=-75
                    load_playfield(level)
                    game = RUNNING
                    print ('start screen exit --> loading level %i'%level)
                    disp.clear()
                    
                if p_pressed == 0 and button_bottom_right == True and (score!=0 or lifes!=1): # new game
                    p_pressed = pressed
                    print('new!')
                    light_level=-75
                    level = 1
                    score = 0
                    lifes = 1
                    save_score()
                    loop_count = 0
                    game = RUNNING
                    load_playfield(level)
                    print ('start screen exit --> loading level %i'%level)
                    disp.clear()
                

            #.................................... game is running

            if game == RUNNING:
                disp.clear()
                game_engine()
                if pressed!=0 and p_pressed==0:
                    disp.clear()
                    scroll_text=0
                    pause_count=0
                    game = PAUSED
                    
            #.................................... game is paused   
            if game == PAUSED:
                if pause_count==0:
                    save_score()
                calib_return = PAUSED
                show_pause_screen()

                #----- check buttons -----
                if pause_count>3 and p_pressed == 0 and button_bottom_left  == True: # calibrate
                    print('calib!')
                    game = CALIB
                    disp.clear()
                    
                if pause_count>3 and p_pressed == 0 and button_top_right    == True: # play
                    p_pressed = pressed
                    print('play!')
                    clamp_count = 10
                    light_level=-75
                    game = RUNNING
                    disp.clear()
                    
                if pause_count>3 and p_pressed == 0 and button_bottom_right == True and (score!=0 or lifes!=1): # new
                    p_pressed = pressed
                    print('new!')
                    clamp_count = 10
                    light_level=-75
                    level = 1
                    score = 0
                    lifes = 1
                    loop_count = 0
                    save_score()
                    reset_playfield()
                    load_playfield(level)
                    print ('pause screen exit --> loading level %i'%level)
                    game = RUNNING
                    disp.clear()
            else:
                pause_count=0



            #.................................... game over [ because of falling ]

            if (falling > 21): 
                save_score()
                reset_playfield()
                load_playfield(level)
                clamp_count = 10
                player_x = player_origin_x
                player_y = player_origin_y
                player_x_speed = 0.0 # speed of player
                player_y_speed = 0.0
                loop_count=0
                falling = 0
                winning = 0
                player_on_blocks = True
                led_timer=11
                p_led_timer=11
                
            #.................................... game over [ because of timeout ]
                
            elif (game==TIMEOUT): 
                disp.clear()
                game_engine()
                timeout_timer += 1

                if (timeout_timer<20 and timeout_timer>1):
                    show_timeout(x_player_screen,y_player_screen)
                
                if (timeout_timer>20):
                    reset_playfield()
                    load_playfield(level)
                    clamp_count = 10
                    timeout_timer = 0
                    player_x = player_origin_x
                    player_y = player_origin_y
                    player_x_speed = 0.0 # speed of player
                    player_y_speed = 0.0
                    loop_count=0
                    falling = 0
                    winning = 0
                    player_on_blocks = True
                    led_timer=11
                    p_led_timer=11 
                    save_score()
                    game = RUNNING
                    
            #.................................... game is won: calculate and show score
            elif (game==WON):
                disp.clear()
                game_engine()


            #.................................... show bonus next to player
                
                if winning_time <= max_time:     # add up bonus
                    bonus += 2+level//2
                    winning_time += 20
                    show_bonus_timer = 10        
                    if(loop_count%2==0):
                        vibra.vibrate(7)
                        


                for led in range(11):            # reduce remaining led bar 
                    if led_unit*led > max_time - winning_time:
                        leds.prep(led, (0,0,0))
                    else:
                        leds.prep(led, playfield_color)
                        led_timer = led
                leds.update()
                    
                
                if show_bonus_timer==0:          # bonus is counted up / save score
                    score += bonus
                    bonus = 0
                    
                    if score > hiscore:          # save hiscore
                        hiscore = score
                        hilifes = lifes


                    if next_level==True:
                    
                        level += 1              # level up, reset values
                        clamp_count = 10
                        print ('level up:%i'%level)
                        reset_playfield()
                        load_playfield(level)
                        player_x = player_origin_x
                        player_y = player_origin_y
                        player_x_speed = 0.0  
                        player_y_speed = 0.0
                        loop_count=0
                        falling = 0
                        winning = 0
                        player_on_blocks = True
                        led_timer=11
                        p_led_timer=11 
                        save_score()
                        game = RUNNING
                    
                    else:
                    
                        print("Game complete" , posx=0,  posy=20, font=display.FONT16)

                if show_bonus_timer>3 and winning>12:           # draw bonus on screen
                    show_bonus(bonus, x_player_screen, y_player_screen, show_bonus_timer)
                    

                show_bonus_timer-=1
                
            #..................................... update display and led's
            #disp.print("%i " % (pause_count)   , posx=15, posy=30, font=display.FONT16)
            disp.update()
            leds.update()
            










	
