leaves.py

Created by florian-allard

Created on July 24, 2025

13.9 KB

Jeu Leaves, adapté pour calculatrice par Afyu.

Le jeu Leaves a été imaginé par IsawU : Présentation en anglais : https://www.youtube.com/watch?v=RQx2V76qQfg Présentation en français : https://www.youtube.com/watch?v=gjh7OLO0V3Y Modèle d’impression 3D : https://www.printables.com/model/72515-abstract-strategy-game-leaves/files )

Chacun des deux joueurs commence la partie avec 10 feuilles, qu’il faut placer autour des 5 branches initiales. À tour de rôle, chacun des joueur place 2 de ses feuilles. Chaque placement de feuille est suivi d’une poussée des tuiles adjacentes (toutes celles qui sont alignées et jointives), dans une direction. La poussée de la 2ème feuille posée doit être effectuée perpendiculairement à la poussée de la 1ère feuille posée, avec une rotation d’un quart de tour dans le sens des aiguilles d’une montre. Il est interdit de placer une feuille entre deux tuiles puis de pousser vers une des deux tuiles qui encadrent (on ne coupe pas l’arbre en deux) mais au contraire, on peut pousser depuis l’extérieur vers l’intérieur dans le but de réduire la taille d’un espace entre deux tuiles.

Le but est d’avoir le plus grand nombre possible de feuilles encore accrochées à une branche à la fin de la partie (après le placement de toutes les feuilles des joueurs) : c’est-à-dire les feuilles qui sont à côté d’une branche, mais pas en diagonale. Ensuite, les feuilles qui ne sont pas (ou plus) accrochées tombent et on compte le nombre de feuilles encore accrochées.


# jeu Leaves, adapté pour calculatrice par Afyu du jeu Leaves initialement imaginé par IsawU (https://www.youtube.com/watch?v=RQx2V76qQfg
# https://www.youtube.com/watch?v=gjh7OLO0V3Y
# Modèle d'impression 3D : https://www.printables.com/model/72515-abstract-strategy-game-leaves/files )

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

def dessine_curseur(x,y,clr,mode_mini=0):
  dX=65+(15-4*mode_mini)*x-1
  dY=25+(15-4*mode_mini)*y-1
  for i in range(2):
    fill_rect(dX,dY+(14-4*mode_mini)*i,15-4*mode_mini,1,clr)
    fill_rect(dX+(14-4*mode_mini)*i,dY,1,15-4*mode_mini,clr)

def efface_case(i,j,mode_mini,clr=(230,230,230)):
  dX=int(65+1+(15-4*mode_mini)*i)
  dY=int(25+1+(15-4*mode_mini)*j)
  fill_rect(dX-1,dY-1,13-4*mode_mini,13-4*mode_mini,clr)

def dessine_case(i,j,nom=" ",mode_mini=0):
  dX=int(65+1+(15-4*mode_mini)*i)
  dY=int(25+1+(15-4*mode_mini)*j)
  efface_case(i,j,mode_mini)
  #fill_rect(dX-1,dY-1,13-4*mode_mini,13-4*mode_mini,(220,220,220))
  if nom==" ":
    nom=("","branche","chene","erable")[Plateau[j][i]]
  
  if nom=="branche":
    if mode_mini:
      motif=("MFFFFMF","MMFFMMM","FMFMMFF","FMMMFFM","FMMMMMF","FMMFFFF","MMFFFFF")
    else:
      motif=("FFFFMFFFFMF","FMFFMMFMMMF","FMFFFMFMFFF","FMMFFMMMMMM","FFMFFMMMFFF","FFMFMMFFFFF","FFMMMFFFMMF","FFMMMFFMMFF","FFMMMMMMFFF","FMMMFFFFFFF","MMMFFFFFFFF")
  elif nom=="chene":
    if mode_mini:
      motif=("FFFFVVV","FFFVVVV","FFVVVVV","FVVVVVF","FVVVVFF","VVVVFFF","VVFFFFF")
    else:
      motif=("FFFFFFFVVVV","FFFFFVVVVVV","FFFFFVVVVVV","FFFVVVVVVVV","FFFVVVVVVVF","FVVVVVVVVVF","FVVVVVVVFFF","FVVVVVVVFFF","FVVVVVFFFFF","VVVVVVFFFFF","VVFFFFFFFFF")
  elif nom=="erable":
    if mode_mini:
      motif=("FFFRFFF","FFRRRFF","RRRRRRR","FRRRRRF","FFRRRFF","FRRRRRF","RRFRFRR")
    else:
      motif=("FFFFFRFFFFF","FFFFRRRFFFF","FFFFRRRFFFF","RRRFRRRFRRR","RRRRRRRRRRR","FRRRRRRRRRF","FFRRRRRRRFF","FFFRRRRRFFF","FFRRRRRRRFF","FRRRFRFRRRF","FFFFFRFFFFF")
  else:
    motif=("F"*(11-4*mode_mini),)*(11-4*mode_mini)
  for ligne in range(11-4*mode_mini):
    for colonne in range(11-4*mode_mini):
      clr=("w","brown",(50,150,30),"r")[("F","M","V","R").index(motif[ligne][colonne])]
      if clr != "w":
        set_pixel(dX+colonne,dY+ligne,clr)


def coup_valide(direction):
  if direction=="droite":
    return sum(Plateau[Yc][:Xc])==0 and Plateau[Yc][Xc+1]!=0 #and any(Plateau[Yc+i][Xc+1]==1 for i in (-1,0,1))
  if direction=="gauche":
    return sum(Plateau[Yc][Xc+1:])==0 and Plateau[Yc][Xc-1]!=0 #and any(Plateau[Yc+i][Xc-1]==1 for i in (-1,0,1))
  if direction=="haut":
    return sum(Plateau[i][Xc] for i in range(Yc+1,H))==0 and Plateau[Yc-1][Xc]!=0 #and any(Plateau[Yc-1][Xc+i]==1 for i in (-1,0,1))
  if direction=="bas":
    return sum(Plateau[i][Xc] for i in range(0,Yc))==0 and Plateau[Yc+1][Xc]!=0 #and any(Plateau[Yc+1][Xc+i]==1 for i in (-1,0,1))

# dessin du plateau, avec les 5 branches initiales
def dessine_plateau():
  for i in range(L):
    for j in range(H):
      dessine_case(i,j," ",mode_mini)

def dessine_motif(motif,clr=""):
  if motif=="|":
    for j in range(11,16):
      if clr=="w":
        efface_case(20,j,1,"w") # mode_mini
      else:
        dessine_case(20,j,"branche",1) # mode_mini
  if motif=="U":
    for (i,j) in ((19,14),(19,15),(20,15),(21,15),(21,14)):
      if clr=="w":
        efface_case(i,j,1,"w") # mode_mini
      else:
        dessine_case(i,j,"branche",1) # mode_mini
  if motif=="+":
    for (i,j) in ((19,14),(20,14),(21,14),(20,13),(20,15)):
      if clr=="w":
        efface_case(i,j,1,"w") # mode_mini
      else:
        dessine_case(i,j,"branche",1) # mode_mini


# arbre en forme de coeur
#for i in range(4):
#  dessine_case(-1.6,4+i,"branche")
#  dessine_case(-2.6,3+i,"erable")
#  dessine_case(-0.6,3+i,"chene")
#  dessine_case(-3.6,4+i%2,"erable")
#  dessine_case(0.4,4+i%2,"chene")

# arbres des côtés
for j in (0,1):
  for i in range(8):
    dessine_case(-2.6+17*j,1.8+i,"branche")
  for i in range(6):
    dessine_case(-3.6+2*j+(2-4*j)*(i%2==1)+17*j,1.8+i,("chene","erable")[i%2])

# feuilles en haut et en bas
for j in (0,1):
  for k in range(j,11+j):
    dessine_case(0.9+k-j,-0.8+11.25*j+k%2,("chene","erable")[k%2])

L=13
H=13
Plateau=[[0]*L for k in range(H)]
motif_initial=2 # | ou U ou +
mode_mini=0


draw_string("Leaves",130,70,"purple")
draw_string("(feuilles)",110,95,"purple")
draw_string("Valider avec OK",85,135,"purple")
for k in range(2): 
  fill_rect(60,55+k*111,200,1,"g")

while not (keydown(KEY_OK) or keydown(KEY_EXE)):
  for k in range(1,20):
    draw_string("Valider avec OK",85,135,(140+10*abs(10-k),25*abs(10-k),200))
    if keydown(KEY_OK) or keydown(KEY_EXE):break
    sleep(0.1)
while keydown(KEY_OK) or keydown(KEY_EXE):1

fill_rect(0,0,320,222,"w")

draw_string("Présentation",100,10,(80,150,50))

Regles=(("Le jeu débute avec 5 branches","(  ) placées sur le plateau.","Chaque joueur a 10 feuilles.","","A tour de rôle, les joueurs","placent puis poussent 2 de","leurs feuilles, en tentant de","les maintenir accrochées aux","branches (pas en diagonale)."),\
("La poussée de la 2ème feuille","placée s'effectue perpendi-","culairement à la poussée de","la 1ère feuille placée, en","tournant dans le sens des","aiguilles d'une montre.","","On ne peut pas placer une","feuille entre deux tuiles."),\
("En fin de partie, toutes les","feuilles qui ne sont plus","accrochées aux branches","tombent et sont retirées du","jeu. Le gagnant est le joueur","qui a le plus de feuilles","encore accrochées.","","Choix du motif initial : "))
for i in range(3):
  for k in range(len(Regles[i])):
    draw_string(Regles[i][k],20,33+18*k,"b")

  draw_string("Page "+str(i+1)+"/3       → OK",120,200,(80,150,50))
  if i==0:
    dessine_case(-2.1,1.9,"branche")
    for k in range(10):
      dessine_case(-3.33+0.94*k,4.3,"chene")
      dessine_case(6.8+0.94*k,4.3,"erable")
  if i==2:
    draw_string("<    >",260,177,"orange")
  
  chg=1
  while not (keydown(KEY_EXE) or keydown(KEY_OK)):
    if i==2:
      if chg:
        dessine_motif("|U+"[motif_initial],"w")
        #draw_string("|U+"[motif_initial],285,177,"w")
        motif_initial=(motif_initial+chg)%3
        dessine_motif("|U+"[motif_initial])
        #draw_string("|U+"[motif_initial],285,177,"purple")
        sleep(0.2)
      chg=keydown(KEY_RIGHT)-keydown(KEY_LEFT)
  while keydown(KEY_EXE) or keydown(KEY_OK):1

  fill_rect(0,30,320,222,"w")

fill_rect(0,0,320,30,"w")

# dessin des 10 feuilles de chaque joueur
for joueur in (0,1):
  for i in range(10):
    dessine_case(-3+(i%2)+17*joueur,3+(i//2),("chene","erable")[joueur])
    dessine_case(2+2*(i%5),11*joueur,"branche")

motif="|U+"[motif_initial]
if motif=="|":
  for i in range(5):
    Plateau[i+L//2-2][H//2]=1 # branche
if motif=="U":
  for (i,j) in ((19,14),(19,15),(20,15),(21,15),(21,14)):
    Plateau[j-9][i-14]=1 # branche
if motif=="+":
  for (i,j) in ((19,14),(20,14),(21,14),(20,13),(20,15)):
    Plateau[j-8][i-14]=1 # branche

Xc,Yc=L//2+1,H//2
chgX,chgY=-1,0
joueur=0
tour=1 # 0 ou 1
precedente_direction="gauche"
#mode_mini=0

dessine_plateau()

while sum([Plateau[n].count(2) for n in range(H)])<10: # tant qu'on n'a pas placé 10 feuilles vertes
  # passage en mode compact, lorsque nécessaire
  if sum(Plateau[1])!=0 or sum(Plateau[-2])!=0 or sum(Plateau[k][1] for k in range(H))!=0 or sum(Plateau[k][-2] for k in range(H))!=0:
    mode_mini=1
    fill_rect(65,25,13*15,13*15,"w")
    if sum(Plateau[1])!=0: # il va manquer des lignes en haut
      Plateau=[[0]*L for k in range(2)]+Plateau
      H+=2
      Yc+=1
    if sum(Plateau[-2])!=0: # il va manquer des lignes en bas
      Plateau=Plateau+[[0]*L for k in range(2)]
      H+=2
      Yc+=1
    if sum(Plateau[k][1] for k in range(H))!=0:
      for ligne in range(H): # il va manquer des colonnes à gauche
        Plateau[ligne]=[0]*2+Plateau[ligne]
      L+=2
      Xc+=1
    if sum(Plateau[k][-2] for k in range(H))!=0:
      for ligne in range(H): # il va manquer des colonnes à droite
        Plateau[ligne]=Plateau[ligne]+[0]*2
      L+=2
    #  Plateau=[[0]*(L+10) for k in range(5)]+Plateau+[[0]*(L+10) for k in range(5)]
    dessine_plateau()

  
  Nrestant=9-sum([Plateau[n].count(2+joueur) for n in range(H)])
  dessine_curseur(-3+Nrestant%2+17*joueur,3+Nrestant//2,"b")
  if tour==0 and Nrestant>0: # curseur autour de la 2ème feuille à jouer
    dessine_curseur(-3+(Nrestant-1)%2+17*joueur,3+(Nrestant-1)//2,"b")
  # choix de l'emplacement
  draw_string(" Choix de l'emplacement ",40,5,"b")
  if chgX or chgY:
    dessine_curseur(Xc,Yc,"w",mode_mini)
    Xc=(Xc+chgX-1)%(L-2)+1 # on évite les bords de Plateau pour éviter les conflits avec des voisins hors bornes
    Yc=(Yc+chgY-1)%(H-2)+1
    dessine_curseur(Xc,Yc,"rb"[Plateau[Yc][Xc]==0 and (((tour==0 or sum([Plateau[n].count(2) for n in range(H)])==0) and any(coup_valide(direction) for direction in ("droite","gauche","haut","bas"))) or (tour==1 and coup_valide(("haut","droite","bas","gauche")[("gauche","haut","droite","bas").index(precedente_direction)]) ))],mode_mini) # 4 directions possibles pour le tout premier coup et le premier coup de chaque joueur, et seulement les directions perpendiculaires au premier coup pour le deuxième coup de chaque joueur
    sleep(0.2)
  chgX=keydown(KEY_RIGHT)-keydown(KEY_LEFT)
  chgY=keydown(KEY_DOWN)-keydown(KEY_UP)
  if (keydown(KEY_OK) or keydown(KEY_EXE)) and Plateau[Yc][Xc]==0 and (((tour==0 or sum([Plateau[n].count(2) for n in range(H)])==0) and any(coup_valide(direction) for direction in ("droite","gauche","haut","bas"))) or (tour==1 and coup_valide(("haut","droite","bas","gauche")[("gauche","haut","droite","bas").index(precedente_direction)]) )):#and any(Plateau[Yc+j][Xc+i]==1 for i in (-1,0,1) for j in (-1,0,1)):#(Plateau[Yc+1][Xc]==1 or Plateau[Yc-1][Xc]==1 or Plateau[Yc][Xc+1]==1 or Plateau[Yc][Xc-1]==1): # on est sur une case vide, adjacente à une branche
    draw_string(" Choix de la direction ",45,5,"b")
    Plateau[Yc][Xc]=joueur+2 # 2 ou 3
    dessine_case(Xc,Yc," ",mode_mini)
    # on efface la feuille de la réserve du joueur concerné
    dessine_curseur(-3+Nrestant%2+17*joueur,3+Nrestant//2,"w")
    dessine_case(-3+Nrestant%2+17*joueur,3+Nrestant//2,"")
    while keydown(KEY_OK) or keydown(KEY_EXE):1
    while 1:
      if keydown(KEY_RIGHT) and coup_valide("droite") and (tour==0 or sum([Plateau[n].count(3) for n in range(H)])==0 or (tour==1 and precedente_direction=="haut")):#sum(Plateau[Yc][:Xc])==0 and Plateau[Yc][Xc+1]!=0 and any(Plateau[Yc+i][Xc+1]==1 for i in (-1,0,1)):
        for k in range(Plateau[Yc].index(0,Xc),Xc,-1):
          Plateau[Yc][k]=Plateau[Yc][k-1]
          dessine_case(k,Yc," ",mode_mini)
        Plateau[Yc][Xc]=0
        dessine_case(Xc,Yc," ",mode_mini)
        precedente_direction="droite"
        joueur=(joueur+tour%2)%2
        tour=(tour+1)%2
        break
      if keydown(KEY_LEFT) and coup_valide("gauche") and (tour==0 or sum([Plateau[n].count(3) for n in range(H)])==0 or (tour==1 and precedente_direction=="bas")):#sum(Plateau[Yc][Xc+1:])==0 and Plateau[Yc][Xc-1]!=0 and any(Plateau[Yc+i][Xc-1]==1 for i in (-1,0,1)):
        for k in range(L-1-Plateau[Yc][::-1].index(0,L-1-Xc),Xc):
          Plateau[Yc][k]=Plateau[Yc][k+1]
          dessine_case(k,Yc," ",mode_mini)
        Plateau[Yc][Xc]=0
        dessine_case(Xc,Yc," ",mode_mini)
        precedente_direction="gauche"
        joueur=(joueur+tour%2)%2
        tour=(tour+1)%2
        break
      if keydown(KEY_UP) and coup_valide("haut") and (tour==0 or sum([Plateau[n].count(3) for n in range(H)])==0 or (tour==1 and precedente_direction=="gauche")):#sum(Plateau[i][Xc] for i in range(Yc+1,H))==0 and Plateau[Yc-1][Xc]!=0 and any(Plateau[Yc-1][Xc+i]==1 for i in (-1,0,1)):
        for k in range(H-1-[Plateau[i][Xc] for i in range(H)][::-1].index(0,H-1-Yc),Yc):
          Plateau[k][Xc]=Plateau[k+1][Xc]
          dessine_case(Xc,k," ",mode_mini)
        Plateau[Yc][Xc]=0
        dessine_case(Xc,Yc," ",mode_mini)
        precedente_direction="haut"
        joueur=(joueur+tour%2)%2
        tour=(tour+1)%2
        break
      if keydown(KEY_DOWN) and coup_valide("bas") and (tour==0 or sum([Plateau[n].count(3) for n in range(H)])==0 or (tour==1 and precedente_direction=="droite")):#sum(Plateau[i][Xc] for i in range(0,Yc))==0 and Plateau[Yc+1][Xc]!=0 and any(Plateau[Yc+1][Xc+i]==1 for i in (-1,0,1)):
        for k in range([Plateau[i][Xc] for i in range(H)].index(0,Yc),Yc,-1):
          Plateau[k][Xc]=Plateau[k-1][Xc]
          dessine_case(Xc,k," ",mode_mini)
        Plateau[Yc][Xc]=0
        dessine_case(Xc,Yc," ",mode_mini)
        precedente_direction="bas"
        joueur=(joueur+tour%2)%2
        tour=(tour+1)%2
        break
dessine_curseur(Xc,Yc,"w",mode_mini)

# fin de partie, on va déterminer combien de feuilles sont attachées à une branche
# et combien de feuilles vont tomber

draw_string("    Partie terminée    ",45,5,"b")

while not (keydown(KEY_OK) or keydown(KEY_EXE)):1
while keydown(KEY_OK) or keydown(KEY_EXE):1

draw_string(" Les feuilles tombent ",50,5,"orange")

for i in range(H):
  for j in range(L):
    if Plateau[i][j] in (2,3) and not any(Plateau[i+k][j+l]==1 and k%2 != l%2 for k in (-1,0,1) for l in (-1,0,1)):
      dessine_curseur(j,i,"k",mode_mini)
sleep(3)

feuilles_tombees=[0,0]
for i in range(H):
  for j in range(L):
    if Plateau[i][j] in (2,3) and not any(Plateau[i+k][j+l]==1 and k%2 != l%2 for k in (-1,0,1) for l in (-1,0,1)):
      dessine_curseur(j,i,"w",mode_mini)
      joueur=Plateau[i][j]-2
      feuilles_tombees[joueur]+=1
      # on dessine la feuille en la rendant au joueur concerné
      Plateau[i][j]=0
      dessine_case(j,i," ",mode_mini)
      dessine_case(-3+(feuilles_tombees[joueur]-1)%2+17*joueur,3+(feuilles_tombees[joueur]-1)//2,("chene","erable")[joueur])
      #draw_string(str(feuilles_tombees),150,200,"b")
      sleep(1)

if feuilles_tombees[0]==feuilles_tombees[1]:
  draw_string("       Egalité !       ",45,5,"b")
else:
  draw_string("Les feuilles "+("vertes","rouges")[feuilles_tombees[1]<feuilles_tombees[0]]+" ont gagné !",5,5,((50,150,30),"r")[feuilles_tombees[1]<feuilles_tombees[0]])

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.