cyclope_snake.py

Created by cent20

Created on April 23, 2021

9.73 KB


"""
CyclopeSnake v0.5
by Fime
https://workshop.numworks.com/python/fime/cyclope_snake
"""
from kandinsky import *
from random import randint
from ion import *
from time import *

LOGO=[
"011101001001100101101111",
"110001101010010110001100",
"001101011011110101101000",
"111001001010010100101111"]

INSTRUCTIONS="""Eat the maximum food you can,but
dont touch the edges or an other
part of the snake !
→ arrows : move
→ if you wait a too long time,
the snake gonna be crazy and
will go in the opposite
direction !
"""


#screen config
SCREEN_W=320
SCREEN_H=180

#game vars
START_SPEED=0.2
SIZE=14
HALF_SIZE=SIZE//2
DIRECTION={"L":[-1,0],"U":[0,-1],"R":[1,0],"D":[0,1]}
ANGRY_TIME=5

#colors
BG_COLOR=(255,255,255)
SDW_COLOR=(150,150,150)
EDGE_COLOR=(50,50,50)
COLORS=[[205,150,0],[100,175,0],[0,205,100],[0,150,205],[150,0,205],[205,0,50]]

#grid config
GRID_W=SCREEN_W//SIZE
GRID_H=SCREEN_H//SIZE
  
OFFSET_X=SCREEN_W%SIZE
OFFSET_Y=SCREEN_H%SIZE

LIMIT_X=[OFFSET_X,SCREEN_W-OFFSET_X]
LIMIT_Y=[OFFSET_Y,SCREEN_H-OFFSET_Y]

START_PO=[GRID_W//2*SIZE+HALF_SIZE+OFFSET_X//2,GRID_H//2*SIZE+HALF_SIZE+OFFSET_Y//2]

def game():
  """The game fonction.
  snake parts: list of the snake parts ([x],[y],[part color])
  snake length: actual limit size of the snake
  game score: score
  best score: best score. saved on snake.sav
  snake position: head's position
  snake direction: head's direction
  food info: food infos ([x],[y],[color])
  food timer: for the limit of the angry
  timer: game timer
  fps: frame par seconds
  """
  
  global s_part,s_len,score,best,po,vec,f_info,f_timer,timer,fps
  
  #draw the (n) element of snake
  def drawElement(n):
    
    #get element info
    inf=list(s_part[n])
    inf.append((inf[2][0]+50,inf[2][1]+50,inf[2][2]+50))
    
    #fill the main square and the detail
    fill_rect(inf[0]-HALF_SIZE,inf[1]-HALF_SIZE,SIZE,SIZE,inf[3])
    fill_rect(inf[0]-HALF_SIZE//2,inf[1]-HALF_SIZE//2,HALF_SIZE//2*2,HALF_SIZE//2*2,inf[2])
    
    drawShadow(inf)
  
  def drawEye():
    
    inf=list(s_part[-1])
    fill_rect(inf[0]-HALF_SIZE//2,inf[1]-HALF_SIZE//2,HALF_SIZE//2*2,HALF_SIZE//2*2,BG_COLOR)
    fill_rect(inf[0]-HALF_SIZE//3+vec[0],inf[1]-HALF_SIZE//3+vec[1],HALF_SIZE//3*2,HALF_SIZE//3*2,EDGE_COLOR)
  
  def drawShadow(inf):
    #draw shadow
    if get_pixel(inf[0],inf[1]+HALF_SIZE)==(248, 252, 248):#if nothing below, draw shadow
      fill_rect(inf[0]-HALF_SIZE,inf[1]+HALF_SIZE,SIZE,SIZE//2,SDW_COLOR)
  
  def hideElement(n):
    
    inf=list(s_part[n])#get part info
    fill_rect(inf[0]-HALF_SIZE,inf[1]-HALF_SIZE,SIZE,SIZE,BG_COLOR)#hide part
    hideShadow(inf)
    
  def hideShadow(inf):
    if get_pixel(inf[0]-HALF_SIZE,inf[1]+HALF_SIZE)==(144,148,144):#if shadow below
      fill_rect(inf[0]-HALF_SIZE,inf[1]+HALF_SIZE,SIZE,SIZE//2,BG_COLOR)
      
    if (get_pixel(inf[0],inf[1]-HALF_SIZE-1)!=(248,252,248)) and not(inf[1]-HALF_SIZE-1<OFFSET_Y):#if part above
      fill_rect(inf[0]-HALF_SIZE,inf[1]-HALF_SIZE,SIZE,HALF_SIZE,SDW_COLOR)
  
  def drawFood():
    #get element info
    inf=list(f_info)
    inf.append([inf[2][0]+50,inf[2][1]+50,inf[2][2]+50])
  
    #fill the main square and the detail
    fill_rect(inf[0]-HALF_SIZE,inf[1]-HALF_SIZE,SIZE,SIZE,inf[3])
    
    fill_rect(inf[0]-HALF_SIZE,inf[1]-HALF_SIZE,HALF_SIZE//2,SIZE,inf[2])
    fill_rect(inf[0],inf[1]-HALF_SIZE,HALF_SIZE//2,SIZE,inf[2])
    
    fill_rect(inf[0]-HALF_SIZE//2-1,inf[1]-HALF_SIZE,HALF_SIZE+1,HALF_SIZE//2+1,EDGE_COLOR)
    fill_rect(inf[0]-HALF_SIZE//2,inf[1]-HALF_SIZE,HALF_SIZE//2,HALF_SIZE//2,(0,200,100))
    fill_rect(inf[0],inf[1]-HALF_SIZE,HALF_SIZE//2,HALF_SIZE//2,(0,150,75))
    
    drawShadow(inf)
  
  def hideFood():
    
    inf=list(f_info)#get part info
    fill_rect(inf[0]-HALF_SIZE,inf[1]-HALF_SIZE,SIZE,SIZE,BG_COLOR)#hide part
    hideShadow(inf)
    
  
  def randPo():
    x=OFFSET_X//2+HALF_SIZE+randint(1,GRID_W-2)*SIZE
    y=OFFSET_Y//2+HALF_SIZE+randint(1,GRID_H-2)*SIZE
    return x,y
  
  def drawScore():
    fill_rect(0,SCREEN_H,320,20,EDGE_COLOR)
    draw_string("Time: {}s".format(int(monotonic()-timer)),0,SCREEN_H,BG_COLOR,EDGE_COLOR)
    draw_string("Score: {} ({})".format(score,best),110,SCREEN_H,BG_COLOR,EDGE_COLOR)
    
  def drawFoodTmr(val):
    lenght=int(val*300/ANGRY_TIME) if val<ANGRY_TIME else 300
    fill_rect(10,SCREEN_H+20,lenght,20,f_info[2])
    if val>=2.5:  
      draw_string("ANGRY",10,SCREEN_H+20,BG_COLOR,f_info[2])
    
  def death_annimation():
    
    for n in range(s_len):
      #hide the elements from the last to the fist
      hideElement(n)
      #s_part.remove(s_part[n])
      sleep(1/s_len)
    
    for y in range(0,222,10):
      fill_rect(0,y,SCREEN_W,10,EDGE_COLOR)
      sleep(0.05)
      
    dead_msg="GAME OVER"
    for y in range(-10,222//2-10,10):
      draw_string(dead_msg,SCREEN_W//2-5*len(dead_msg),y,BG_COLOR,EDGE_COLOR)
      sleep(0.05)
      fill_rect(SCREEN_W//2-5*len(dead_msg),y,10*len(dead_msg),20,EDGE_COLOR)
    draw_string(dead_msg,SCREEN_W//2-5*len(dead_msg),y,BG_COLOR,EDGE_COLOR)
    x,y=SCREEN_H//2,SCREEN_H//2-40

  #init vars
  s_part,s_len,s_color,hue,f_hue,po=[],5,COLORS[0],0,0,list(START_PO)
  f_info, f_timer,angry=[randPo()[0],randPo()[1],COLORS[0]],float(monotonic()),0
  score,key,frame,frame_count,timer,speed=0,"L",1,0,float(monotonic()),START_SPEED
  best=int(readBestScore())
  
  print(str(po))
  #init screen  
  fill_rect(0,0,320,222,EDGE_COLOR)
  fill_rect(OFFSET_X//2,OFFSET_Y//2,SCREEN_W-OFFSET_X,SCREEN_H-OFFSET_Y,(BG_COLOR))
  
  drawFood()
  print("Starting game...")
      
  while True:
    
    frame_count+=1
    
    #frame for move
    if monotonic()-frame>=speed:
      
      #fps
      fps=int(frame_count/(monotonic()-timer))
      
      #reset second count
      frame=monotonic()
      
      vec=list(DIRECTION[key])#get direction
      
      po[0]+=vec[0]*SIZE#change position
      po[1]+=vec[1]*SIZE
      
      #score
      drawScore()
      
      #dead hitbox
      dead=0
      if LIMIT_X[0]>po[0] or po[0]>LIMIT_X[1]:#out of area
        dead=1
      elif LIMIT_Y[0]>po[1] or po[1]>LIMIT_Y[1]:
        dead=1
      if po[0:2] in [s_part[n][0:2] for n in range(len(s_part))]:
          dead=1
          print("touch")
      
      if dead:
        print("Game over...\nFrame count : {}\nGame during : {}\nFps : {}\nScore : {}\nSpeed : {}".format(frame_count,monotonic()-timer,fps,score,speed))
        death_annimation()#game over
        saveScore(best)
        break
      
      else:#normal move
        
        #actualize position
        s_part.append([po[0],po[1],s_color])
        change_color=0
        
        #food hitbox
        if po[0:2]==f_info[0:2]:
          hideFood()
          s_len+=1
          score+=1000
          speed=speed/1.02
          
          f_timer=monotonic()
          fill_rect(0,SCREEN_H+20,320,20,EDGE_COLOR)
          change_color=1
          loop=0
          
          f_hue=0 if f_hue==len(COLORS)-1 else f_hue+1
          f_info[2]=COLORS[f_hue]
          
          while True:
            f_info[0:2]=randPo()[0],randPo()[1]
            loop+=1
            if not f_info[0:2] in [i[0:2] for i in s_part]:
              break
            if loop>GRID_W*GRID_H:
              break
          
          hue=f_hue
          for n in range(len(s_part)):
            s_part[n][2]=COLORS[hue]
            drawElement(n)
          drawFood()
          
        score+=20
        if score>best:
          best=score
        
        if monotonic()-f_timer>=ANGRY_TIME:
          angry=1
          change_color=1
          hue=0 if hue==len(COLORS)-1 else hue+1
          draw_string("CRAZY !",10,SCREEN_H+20,BG_COLOR, f_info[2])
        
        else:
          angry=0
          drawFoodTmr(monotonic()-f_timer)
          
        s_color=COLORS[hue]
        if len(s_part)>=2:
          drawElement(-2)
        drawElement(-1)
        drawEye()
    
    if len(s_part)>s_len:#len limit
      hideElement(0)
      s_part.remove(s_part[0])
    
    #controls
    if keydown(KEY_LEFT) and vec[0]==0:
      key="R" if angry else "L"
    elif keydown(KEY_UP) and vec[1]==0:
      key="D" if angry else "U"
    elif keydown(KEY_RIGHT) and vec[0]==0:
      key="L" if angry else "R"
    elif keydown(KEY_DOWN) and vec[1]==0:
      key="U" if angry else "D"
  
  ######game over
  text="\t\tScore :{}\n\t\tBest Score :{}".format(score,best)
  x=320//2-(len(LOGO[0])*10)//2
  drawLogo(x,10,10,BG_COLOR)
  draw_string(text,0,150,BG_COLOR,EDGE_COLOR)
  while True:
    if keydown(KEY_OK):
      while keydown(KEY_OK):
        pass
      break
    if int(monotonic())%2==1:
      draw_string("press <OK>",220,200,(255,100,100),EDGE_COLOR)
    else:
      fill_rect(220,200,140,20,EDGE_COLOR)
  game()

  

def main_menu():
  """the main menu"""
  fill_rect(0,0,320,222,EDGE_COLOR)
  x=320//2-(len(LOGO[0])*10)//2
  drawLogo(x,5,10,BG_COLOR)
  draw_string(INSTRUCTIONS,0,60,(150,150,150),EDGE_COLOR)
  while True:
    if keydown(KEY_OK):
      while keydown(KEY_OK):
        pass
      break
    if int(monotonic())%2==1:
      draw_string("press <OK>",220,200,(255,50,50),EDGE_COLOR)
    else:
      fill_rect(220,200,140,20,EDGE_COLOR)
  game()
  
  
def drawLogo(x,y,size,color):
  """draw the logo at [x,y]
  with the specified size for each pixel
  and the specified color
  """
  for yOf in range(len(LOGO)):
    for xOf in range(len(LOGO[yOf])):
      if LOGO[yOf][xOf]=="1":
        fill_rect(x+xOf*size,y+yOf*size,size,size,color)

def readBestScore():
  """
  read and return the best score
  saved in snake.sav
  return 0 if reading failed
  """
  try:
    file=open("snake.sav","r")
    best=file.readline()
    file.close()
    return int(best)
  except:
    return 0

def saveScore(score):
  """
  erase the actual best score in snake.sav
  then write [score] on it.
  """
  try:
    file=open("snake.sav","w")
    file.truncate(0)
    file.write(str(score))
    file.close()
  except:
    pass

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.