pianota.py

Created by sandraev

Created on February 01, 2023

12.1 KB

Pianota 🎶 Un remix de PianoTiles en Python pour la calculatrice graphique Numworks (Omega/Upsilon). https://git.dynacloud.ynh.fr/lleblanc/pianota

🔥 Installation Pianota dépend de la bibliothèque graphique NwTK, disponible à cette adresse : https://git.dynacloud.ynh.fr/lleblanc/nwtk

🐛 Suggestions ou bug trouvé Si jamais vous avez une merveilleuse idée d’amélioration ou bien que vous tombez subitement sur un fonctionnement anormal, n’hésitez surtout pas à ouvrir un ticket ! Merci d’avance de votre contribution ! 😉


from kandinsky import *
from random import randint
from ion import *
from time import sleep
from os import listdir


#####################################
# At first we define some constants #
#####################################

PROG_VER = "1.9.2"

SCREEN_W = 320
SCREEN_H = 222

KEYS = [KEY_COSINE,KEY_TANGENT,KEY_PI,KEY_SQRT]
COLORS = [([22,102,222],[80,160,255]),
([140,80,180],[215,155,255]),
([180,50,50],[255,125,125]),
([180,60,30],[255,135,100]),
([180,140,0],[255,215,75]),
([0,180,40],[75,255,115]),
([180,180,180],[240,240,240])]

BG_COLOR = [0,0,0]

NOTE_SPEED = 2
NOTE_SPACE = 6
NOTE_H = 46

POLY_MODE = 0
POLY_ACCURACY = 9

PERFECT_MODE = 1
PERFECT_NB = 5

INFINITE_MODE = 0

LVL_LEN = 100


#########################
# We define our classes #
#########################

class Button():
    """Aiming at replace the one define in NWTK"""
    def __init__(self,x,y,txt,txt_color,btn_color,bg_color):
        self.focused = False
        self.x,self.y = x,y
        self.txt = txt
        self.txt_color = txt_color
        self.btn_color = btn_color
        self.bg_color = bg_color
    def draw(self):
        real_btn_color = self.btn_color
        real_txt_color = self.txt_color
        if self.focused:
            real_btn_color = self.txt_color
            real_txt_color = self.btn_color
        fill_rect(self.x,self.y,len(self.txt)*7 + 4,14 + 4,real_btn_color)
        draw_string(self.txt,self.x + 2,self.y + 2,real_txt_color,real_btn_color,1)
        set_pixel(self.x,self.y,self.bg_color)
        set_pixel(self.x + len(self.txt)*7 + 4 - 1,self.y,self.bg_color)
        set_pixel(self.x + len(self.txt)*7 + 4 - 1,self.y + 14 + 4 - 1,self.bg_color)
        set_pixel(self.x,self.y + 14 + 4 - 1,self.bg_color)

class NoteGrid():
    """Defines a grid and its geometry for tiles"""
    def __init__(self,w,total_column_nb,padding):
        self.total_column_nb = total_column_nb
        self.padding = padding
        self.w = w
        self.x = int(SCREEN_W/2-self.w/2)
        self.col_w = int(self.w/self.total_column_nb)
        self.draw()
    def draw(self):
        draw_line(self.x,0,self.x,SCREEN_H,[60,60,60])
        draw_line(int(self.x+self.col_w*1),0,int(self.x+self.col_w*1),SCREEN_H,[60,60,60])
        draw_line(int(self.x+self.col_w*2),0,int(self.x+self.col_w*2),SCREEN_H,[60,60,60])
        draw_line(int(self.x+self.col_w*3),0,int(self.x+self.col_w*3),SCREEN_H,[60,60,60])
        draw_line(int(self.x+self.col_w*4),0,int(self.x+self.col_w*4),SCREEN_H,[60,60,60])

class Note():
    """Defines a single tile"""
    def __init__(self,Grid,column_nb,y,h,color,active_color,bg_color):
        self.Grid = Grid
        self.column_nb = column_nb
        self.x = Grid.x + Grid.col_w * column_nb + Grid.padding
        self.w = Grid.col_w - Grid.padding * 2
        self.y,self.h = y,h
        self.color,self.bg_color = color,bg_color
        self.active_color = active_color
        self.speed = 1
        self.active = False
    def step(self):
        if self.y < 200:
            if self.active:
                fill_rect(self.x,self.y,self.w,self.speed,self.active_color)
            else:
                fill_rect(self.x,self.y,self.w,self.speed,self.color)
        fill_rect(self.x,self.y-self.h,self.w,self.speed,self.bg_color)
    def flash(self):
        if not self.active:
            fill_rect(self.x,self.y-self.h,self.w,self.h,self.color)
        else:
            fill_rect(self.x,self.y-self.h,self.w,self.h,self.active_color)
        if self.y > 200:
            fill_rect(self.x,200,self.w,self.y-200,self.bg_color)

class TouchLine():
    """Defines the deadline for the tiles"""
    def __init__(self,y,h,color):
        self.y,self.h = y,h
        self.color = color
    def draw(self):
        fill_rect(0,self.y,320,self.h,self.color)

class ColTxt():
    """Defines a single text align within a column"""
    def __init__(self,y,Grid,column_nb,txt,color,active_color,bg_color):
        self.y = y
        self.Grid = Grid
        self.column_nb = column_nb
        self.txt = txt
        self.color = color
        self.active_color = active_color
        self.bg_color = bg_color
        self.active = False
    def draw(self,color=None):
        if color == None:
            color = self.active_color
        if self.active:
            draw_string(self.txt,int(self.Grid.x+self.Grid.col_w*self.column_nb)+int(self.Grid.col_w/2-len(self.txt)*7/2),self.y,color,self.bg_color,1)
        else:
            draw_string(self.txt,int(self.Grid.x+self.Grid.col_w*self.column_nb)+int(self.Grid.col_w/2-len(self.txt)*7/2),self.y,self.color,self.bg_color,1)


#######################################
# Now we define some useful functions #
#######################################

def transtion():
  for i in range(222):
    draw_line(0,i,80,i,[10,10,40])
    sleep(0.0005)
  for i in range(222):
    draw_line(240,i,320,i,[10,10,40])
    sleep(0.0005)
  for i in range(222):
    draw_line(80,i,160,i,[10,10,40])
    sleep(0.0005)
  for i in range(222):
    draw_line(160,i,240,i,[10,10,40])
    sleep(0.0005)
  sleep(0.2)

def load_score():
  if "nota.score" in listdir():
    file = open("nota.score","r")
    score = file.readline()
    file.close()
  else:
    file = open("nota.score","w+")
    score = 0
    file.close()
  if score == "":
    score = 0
  return score

def save_score(score):
  file = open("nota.score","w")
  file.truncate(0)
  file.write(str(score))
  file.close()

def main_menu():
  fill_rect(0,0,320,222,"black")

  notes = []
  color_nb = 0
  pixel = 0

  note_grid = NoteGrid(180,4,4)
  touch_line = TouchLine(200,2,[212,42,26])

  txt = "PiaNOTA"
  for i in range(len(txt)):
    draw_string(txt[i],int(125+i*10),50,[0,0,0],COLORS[i][1])
  draw_string("A PianoTiles remix!",92,70,[200,200,200],[0,0,0],1)

  keys = [ColTxt(160,note_grid,0,"[cos]",[100,100,100],[240,240,240],[0,0,0]),
  ColTxt(160,note_grid,1,"[tan]",[100,100,100],[240,240,240],[0,0,0]),
  ColTxt(160,note_grid,2,"[pi]",[100,100,100],[240,240,240],[0,0,0]),
  ColTxt(160,note_grid,3,"[sqrt]",[100,100,100],[240,240,240],[0,0,0])]

  keys_colors = []

  for i in keys:
    i.draw()
    keys_colors.append(COLORS[randint(0,6)][1])

  play_bt = Button(134,120,"PLAY !",[255,255,255],[32,112,232],[0,0,0])
  play_bt.draw()

  draw_string("Best score: ",10,198,[220,60,70],[0,0,0],1)
  draw_string(str(load_score()),90,195,[255,80,90],[0,0,0])

  draw_string("V "+str(PROG_VER),320-12-int((2+len(str(PROG_VER)))*7),198,[255,140,70],BG_COLOR,1)

  while True:
    for i in KEYS:
      if keydown(i):
        keys[KEYS.index(i)].active = True
        keys_colors[KEYS.index(i)] = COLORS[randint(0,6)][1]
      else:
        keys[KEYS.index(i)].active = False
      keys[KEYS.index(i)].draw(keys_colors[KEYS.index(i)])

    if keydown(KEY_OK):
      play_bt.focused = True
      play_bt.draw()
      while keydown(KEY_OK): pass
      play()
      break

def down_message(txt_color,color,txt):
  for i in range(24):
    draw_line(0,200-i,320,200-i,color)
    sleep(0.01)
  draw_string(txt,int(SCREEN_W/2-len(txt)*10/2),180,txt_color,color)
  sleep(2.5)

def game_over(msg=""):
  down_message([220,220,220],[212,42,26],"Game Over")
  print(msg)
  transtion()


################################
# And here is the main program #
################################

def play(starting_score=0):
  #TODO: Remove these (awful!) global variables
  global INFINITE_MODE,POLY_MODE

  transtion()
  fill_rect(0,0,320,222,[0,0,0])

  note_grid = NoteGrid(180,4,4)
  touch_line = TouchLine(200,2,[212,42,26])

  notes = []
  notes_active_screen_nb = 0

  color_nb = 0
  waiting = 0
  score = starting_score
  perfect = 0
  lvl_nb = 1
  best_score = int(load_score())
  pixel = 0
  sleep_time = 0.007
  col_active = {0:0,1:0,2:0,3:0}

  remains = {}
  for i in KEYS:
    remains[i] = [False,0]

  draw_string("Score :",0,0,[220,220,220],[0,0,0],1)
  draw_string(str(score),0,14,[200,120,80],[0,0,0])

  draw_string("Level :",320-49,0,[220,220,220],[0,0,0],1)
  if INFINITE_MODE:
    draw_string("...",320-30-14,14,[0,0,0],COLORS[6][1])
  else:
    draw_string(str(lvl_nb),320-30,14,[0,0,0],COLORS[color_nb][0])

  if NOTE_SPEED % 2 != 0:
    note_speed_margin = NOTE_SPEED-1
  else:
    note_speed_margin = NOTE_SPEED

  while True:
    # Note adding
    if not waiting:
      if pixel % (NOTE_H+NOTE_SPACE+note_speed_margin) == 0:
        last_note_col = randint(0,3)

        if INFINITE_MODE:
          color_nb = randint(0,len(COLORS)-1)

        notes.append(Note(note_grid,last_note_col,-(NOTE_H+NOTE_SPACE),NOTE_H,COLORS[color_nb][0],COLORS[color_nb][1],[0,0,0]))

        # Poly-Mode feature !
        if POLY_MODE:
          if randint(0,6)+randint(0,6) == POLY_ACCURACY:
            note_col = randint(0,3)
            while note_col - last_note_col < 2 and note_col - last_note_col > -2:
              note_col = randint(0,3)
            notes.append(Note(note_grid,note_col,-(NOTE_H+NOTE_SPACE),NOTE_H,COLORS[color_nb][0],COLORS[color_nb][1],[0,0,0]))

    # Key listener
    for i in KEYS:
      if keydown(i) and not remains[i][0]:
        col_active[KEYS.index(i)] = 1
        remains[i] = [True,pixel]
      if remains[i][0]:
        if remains[i][1]+int(NOTE_SPACE*NOTE_SPEED*2) < pixel:
          remains[i][0] = False

    # Pause feature
    if keydown(KEY_OK):
      while keydown(KEY_OK): pass
      draw_string("Game Paused",int(320/2-11*7/2),int(222/2-14/2),[0,0,0],[200,200,200],1)
      while not keydown(KEY_OK): pass
      while keydown(KEY_OK): pass
      draw_string("Game Paused",int(320/2-11*7/2),int(222/2-14/2),[0,0,0],[0,0,0],1)
      for i in notes:
        i.flash()
      touch_line.draw()

    # Note step
    for i in notes:
      i.step()
      i.speed = NOTE_SPEED
      i.y += NOTE_SPEED

      # Note activation
      if not i.active:
        if i.y > 5 and i.y-i.h < touch_line.y: # Note is visible
          if col_active[i.column_nb]:
            i.active = True
            notes_active_screen_nb += 1
            col_active[i.column_nb] = False
            i.flash()

            # Check if this is the last note
            if notes.index(i) > notes_active_screen_nb -1 and not POLY_MODE:
              if score > best_score:
                save_score(score)
              game_over("GO: Note activated was not the last one !")
              print("DBG: Note index was "+str(notes.index(i)))
              return

            # Update score
            score += 1
            if score > best_score:
              draw_string(str(score),0,14,[0,0,0],[220,120,80])
            else:
              draw_string(str(score),0,14,[220,120,80],[0,0,0])

            # Pixel perfect feature
            if PERFECT_MODE:
              if i.y+NOTE_SPACE > touch_line.y and i.y-i.h < touch_line.y:
                perfect += 1
              elif perfect != 0:
                perfect = 0
                draw_string("Perfect!",2,120,BG_COLOR,BG_COLOR,1)

              if perfect > PERFECT_NB:
                score += 1
                if perfect == PERFECT_NB+1:
                  draw_string("Perfect!",2,120,BG_COLOR,COLORS[color_nb][1],1)

      # Note escape
      if i.y > 280:
        if not i.active:
          if score > best_score:
            save_score(score)
          game_over("GO: Note still active !")
          return
        notes_active_screen_nb -= 1
        notes.remove(i)

    # Check for active columns
    for i in col_active:
      if col_active[i]:
        if score > best_score:
          save_score(score)
        game_over("GO: Column activated without reason !")
        return

    # Level change
    if (score/lvl_nb > LVL_LEN) and score != 0:
      if not INFINITE_MODE:
        waiting = 1
    if waiting:
      if len(notes) == 0:
        color_nb += 1
        lvl_nb += 1
        sleep_time -= 0.001
        waiting = 0

        perfect = 0
        draw_string("Perfect!",2,120,BG_COLOR,BG_COLOR,1)

        if lvl_nb > 7:
          down_message([0,0,0],[255,140,70],"INFINITE MODE Enabled !")
          save_score(score)
          transtion()

          sleep(0.5)

          INFINITE_MODE = 1
          POLY_MODE = 1
          play(score)
          return
        else:
          draw_string(str(lvl_nb),320-30,14,BG_COLOR,COLORS[color_nb][0])

    pixel += NOTE_SPEED
    touch_line.draw()
    if not INFINITE_MODE:
      sleep(sleep_time)


# The game loop...
while True:
  main_menu()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.