factors.py

Created by ilyas-r

Created on February 14, 2023

8.41 KB

Factors v1.4.
Python Game for Numworks, all models.
By Ilyas R. & Vincent ROBERT. April 2022.

Learn more about Factors on: nsi.xyz/factors (FR)

Changelog

Factors v1.0 - 14/04/2022:
- Initial game

Factors v1.1 - 15/04/2022:
- Fixed saving of average after retrying a level
- Fixed the disappearance of the top arrow in the menu

Factors v1.2 - 21/04/2022:
- Fixed displaying of average with impossible rounding

Factors v1.3 - 28/08/2022:
- Fixed the disappearance of the cursor when pressing several inputs at the same time when accessing the menu

Factors v1.4 - 19/09/2022 (latest):
- The [OK] key now starts the progress feature
- The [Ans] key is now used to exit the program cleanly


from kandinsky import *
from random import randint
from ion import *

# Factors 1.4 NumWorks, 19.09.2022
# Par Ilyas R. & Vincent ROBERT
# https://nsi.xyz/factors <3

l_c,l_m,cursor,avg = 1,1,0,1.0
last_key,retry,in_level = None,False,False

rec = fill_rect
txt = draw_string

grid,pos = [1+i for i in range(16)],[]
sco = {0:0}
try:
    get_keys()
    os_c = (192,53,53)
except NameError:
    os_c = (255,183,52)

grey_c, dark_c, game_c, white_c = (196,196,196),(42,42,42),(148,113,222),(255,255,255)

# Le tuple `numbers` ainsi que la fonction `draw_number()` viennent d'Eric Schrafstetter
numbers = (31599,18724,29671,31207,18925,31183,31695,18727,31727,31215)
letters = (37583,188271,234063,74903,252783,186351,248271)

def fill(n,s):
    return "0"*(s-len(str(n)))+str(n)

def boot(key=420010100620):
    global l_c,l_m,avg,sco,last_key,retry,in_level
    if (key//1000)%sum(white_c)==key%1000 :
        l_m = int(str(key)[2:5])
        l_c = l_m
        avg = int(str(key)[5:7])+int(str(key)[7:10])/1000
        sco = {0:0,1:1}
        for level in range(1,l_m):
            sco[level] = 1
        for i in range(int((avg-1)*l_m)):
            sco[randint(max(l_m-10,1),max(l_m-1,1))] += 1
        last_key,retry,in_level = None,False,False
        factors()
    else:
        print("Invalid Key! Try again XD")

def generate_key():
    global l_m,avg
    if avg>99 or l_m>999:
        return "No backup possible"
    else:
        code_str = "42"+fill(l_m,3)+fill(int(avg),2)+fill(int((avg%1)*1000),2)
        hash_code = int(code_str)%sum(white_c)
        return code_str+fill(hash_code,3)

def save():
    print("[OK] Save progress feature ^^")
    print(generate_key())
    print("To load data, run")
    print("boot("+generate_key()+")")
    print("Press any key to continue")
    input()

def gui():
    for k in range(7):
        for i in range(19):
            if letters[k]>>i&1==1:
                rec(7+12*k+(i%3)*3,10+(i//3)*3,3,3,os_c)
    txt("Lvl",55,69,dark_c)
    txt("Avg",55,157,dark_c)
    rec(139,200,174,22,os_c)
    txt("Designed by",22,202,grey_c)
    txt("nsi.xyz/factors",152,202,white_c,os_c)

def menu(in_menu=0):
    global l_c,avg,last_key,in_level
    rec(7,145,42,42,grey_c)
    avg_aff = str(round(avg)) if avg>=10 else str(round(avg,1))[:3]
    txt(str(avg_aff),13+5*(10<= avg<100),158,dark_c,grey_c)
    if in_menu==1:
        rec(7,57,42,42,game_c)
        refresh_scoreboard()
        txt("Play",8,37,dark_c)
        txt("Best",55,113,dark_c)
        rec(95,13,42,42*4+6,white_c)
        arrow(90,71,3,os_c)
        for i in range(4):
            for j in range(4):
                rec(139+44*j,13+44*i,42,42,grey_c)
        while not keydown(51) and not keydown(3):
            if keydown(1) and last_key!=1 and l_c!=l_m:
                l_c += 1
                last_key = 1
                refresh_scoreboard()
            if keydown(2) and last_key!=2 and l_c!=1:
                l_c -= 1
                last_key = 2
                refresh_scoreboard()
            if not (keydown(0) or keydown(1) or keydown(2) or keydown(3)):
                last_key = None
        in_level = False
        last_key = 3
    else:
        rec(7,57,42,42,grey_c)
        draw_number(l_c,7+44//2+center(l_c),13+58,dark_c)
        txt("Play",8,37,white_c)
        txt("Best",55,113,white_c)
        rec(0,101,60,42,white_c)
        if l_c==42:
            txt("^_^",55,113,game_c)

def refresh_scoreboard():
    rec(7,57,42,42,game_c)
    draw_number(l_c,7+44//2+center(l_c),13+58,dark_c)
    rec(0,101,42,42,white_c)
    if l_c!=l_m or (sco.get(l_c) is not None):
        txt(str(sco[l_c]),18+4*(sco[l_c]<10),113,dark_c)
        arrow(62,57,1,os_c if l_c!=l_m else white_c)
    else:
        arrow(62,57,1)
    if l_c != 1:
        arrow(62,92,2,os_c)
    else:
        arrow(62,92,2)

def center(nbr):
    if 0<nbr<10:
        ecart = -6
    elif 10<=nbr<100:
        ecart = -12
    elif 100<=nbr<1000:
        ecart = -18
    else:
        ecart = -26
    return ecart

def draw_number(n,x,y,c):
    if n>0:
        for k,j in enumerate(str(n)):
            for i in range(16):
                if numbers[int(j)]>>i&1==1:
                    rec(x+12*k+(i%3)*3,y+(i//3)*3,3,3,c)

def arrow(x,y,d,c=white_c):
    for i in range(6):
        if d==0:
            rec(x+i,y+6-i,1,2+2*i,c)
        if d==1:
            rec(x+6-i,y+i,2+2*i,1,c)
        if d==2:
            rec(x+i,y+i,12-2*i,1,c)
        if d==3:
            rec(x+i,y+i,1,12-2*i,c)

def generate_grid():
    for i in range(16):
        j = randint(1,15)
        grid[i],grid[j] = grid[j],grid[i]

def start_level(re=0,new=0):
    global cursor,pos,retry,l_c,l_m
    if new == 1:
        l_c += 1
        if l_c == l_m + 1:
            l_m += 1
    cursor = l_c
    pos = [0,1]
    arrow(62,57,1)
    arrow(62,92,2)
    arrow(90,71,3)
    arrow(0,71,0)
    rec(95,57,42,42,game_c)
    draw_number(l_c,d_p(pos[0])[0]+44//2+center(l_c),d_p(0,pos[1])[1]+44//2-8,dark_c)
    if not re:
        generate_grid()
    rec(d_p(5)[0],d_p(0,4)[1],0-176,0-176,white_c)
    k = 0
    for i in range(4):
        for j in range(4):
            rec(139+44*j,13+44*i,42,42,game_c)
            draw_number(grid[k],(139+44*j)+44//2+center(grid[k]),
                        (13+44*i)+44//2-8,white_c)
            k += 1
    retry = False

def move(key=3):
    global cursor,pos
    if 0<=pos[0]<=4 and 0<=pos[1]<=3:
        if key==3:
            rec(d_p(pos[0]-1)[0],d_p(pos[0],pos[1])[1],42,42,white_c)
            rec(d_p(pos[0])[0],13,42,42*4+3*2,white_c)
            number_interact=get_number(pos[0]-1,pos[1])
            if cursor%number_interact==0:
                cursor=cursor//number_interact
            else:
                cursor=cursor+number_interact
            rec(d_p(pos[0])[0],d_p(0,pos[1])[1],42,42,game_c)
            draw_number(cursor,d_p(pos[0])[0]+44//2+center(cursor),d_p(0,pos[1])[1]+44//2-8,dark_c)
        else:
            rec(d_p(pos[0])[0],d_p(pos[0],pos[1]+(-1 if key==2 else 1))[1],42,42,white_c)
            rec(d_p(pos[0])[0],d_p(0,pos[1])[1],42,42,game_c)
            draw_number(cursor,d_p(pos[0])[0]+44//2+center(cursor),d_p(0,pos[1])[1]+44//2-8,dark_c)

def get_number(x,y):
    return grid[y*4+x]

def d_p(p_x=0,p_y=0):
    return [95+44*p_x,13+44*p_y]

def level_transition(s):
    global l_c,l_m,avg,sco,pos
    k=0
    while d_p(pos[0])[0]+k != d_p(4)[0]:
        rec(d_p(pos[0])[0]+k,d_p(0,pos[1])[1],42,42,game_c)
        k+=1
    pos=[4,pos[1]]
    k=0
    while d_p(0,pos[1])[1]+k != d_p(0,3)[1]:
        rec(d_p(pos[0])[0],d_p(0,pos[1])[1]+k,42,42,game_c)
        k+=1
    pos=[4,3]
    k=0
    while k!=175:
        rec(d_p(pos[0]+1)[0]-2,d_p(0,pos[1]+1)[1]-2,0-k,0-k,game_c)
        k+=1
    txt("Perfect" if s==1 else "Well done" if s<=avg else "Can do better!!!",150 if s>avg else 185,50,white_c,game_c)
    draw_number(s,210,75,dark_c)
    txt("Retry",156,120,white_c,game_c)
    txt("Next",256,120,white_c,game_c)
    arrow(176,142,0,dark_c)
    arrow(266,143,3,dark_c)
    if l_c in sco:
        sco[l_c]=min(sco[l_c],s)
    else:
        sco[l_c]=s
    somme=0
    for value in sco.values():
        somme+=value
    avg=somme/(len(sco)-1*(len(sco)!=1))
    pos[0]=42

def factors():
    global cursor, pos, last_key, retry, in_level
    while not keydown(51):
        if in_level:
            if keydown(3) and last_key != 3:
                pos[0] += 1
                last_key = 3
                move(3)
            if keydown(0) and last_key != 0 and pos[0] > 0:
                retry = True
                last_key = 0
            elif keydown(0) and last_key != 0 and pos[0] == 0:
                pos[0] -= 1
            if keydown(1) and last_key != 1 and pos[1] > 0:
                pos[1] -= 1
                last_key = 1
                move(1)
            if keydown(2) and last_key != 2 and pos[1] < 3:
                pos[1] += 1
                last_key = 2
                move(2)
            if pos[0] == -1:
                menu(1)
            if retry:
                start_level(1)
                menu()
            if pos[0] == 4 or (cursor == 1 and not ((-1 <= pos[0] <= 0) or 42 <= pos[0] <= 43)):
                level_transition(cursor)
            if pos[0] == 43:
                start_level(0, 1)
                menu()
            if not (keydown(0) or keydown(1) or keydown(2) or keydown(3)):
                last_key = None
            if keydown(4):
                save()
                break
        else:
            gui()
            menu()
            start_level()
            in_level = True

factors()
# Your boot key: