jeux_2048.py

Created by archange

Created on June 28, 2024

14.4 KB

This project is a simple version of the 2048 game.

If you’re curious, or just want to find out how it works or learn more, go to my github : https://github.com/Archange-py/2048-Game


from ion import keydown, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_BACKSPACE, KEY_OK
from kandinsky import draw_string, fill_rect
from random import choice, random
from time import sleep

class Screen: palette = {"Background" : (255,255,255), "PrimaryColor" : (0,0,0), "PrimaryText" : (0,200,200), "SecondaryText" : (255,255,255)}

class Cube:
    style = {2:(204,238,255), 4:(102,170,178), 8:(51,136,140), 16:(0,102,102), 32:(85,153,68), 64:(142,187,45), 128:(179,210,30), 256:(255,255,0), 512:(255,127,0), 1024:(255,64,0), 2048:(255,0,0)}
    def __init__(self, x, y, nbr=2): self.x, self.y, self.nbr = x, y, nbr

    def draw(self, pos_x, pos_y, taille, espace):
        fill_rect(pos_x+self.x*(taille+espace),pos_y+self.y*(taille+espace),taille, taille, Cube.style[self.nbr])
        draw_string(str(self.nbr),pos_x+self.x*(taille+espace)+2,pos_y+self.y*(taille+espace) + int(taille/2)-8,"black", Cube.style[self.nbr])

class Matrice:
    def __init__(self, N=4): self.N, self.score = N, 0 ; self.cubes = [[Cube(x, y, 2 if random() <= 0.85 else 4) for y in range(self.N)] for x in range(self.N)]
    def __getitem__(self, ij): return self.cubes[ij]
    def __len__(self): return self.N * self.N - sum([ligne.count(None) for ligne in self.cubes])
    def __iter__(self): return (self[x][y] for y in range(self.N) for x in range(self.N))

    def getNumberMove(self, cube, ind):
        if ind == "UP":
            m = cube.y
            if cube.y != 0:
                while True:
                    m -= 1
                    if self[cube.x][m] != None: 
                        if self[cube.x][m].nbr != cube.nbr: m += 1
                        break
                    elif m == 0: break
            return cube.y - m
        elif ind == "DOWN":
            m = cube.y
            if cube.y + 1 != self.N:
                while True:
                    m += 1
                    if self[cube.x][m] != None:
                        if self[cube.x][m].nbr != cube.nbr: m -= 1
                        break
                    elif m + 1 == self.N: break
            return abs(cube.y - m)
        elif ind == "LEFT":
            m = cube.x
            if cube.x != 0:
                while True:
                    m -= 1
                    if self[m][cube.y] != None:
                        if self[m][cube.y].nbr != cube.nbr: m += 1
                        break
                    elif m == 0: break
            return cube.x - m
        elif ind == "RIGHT":
            m = cube.x
            if cube.x + 1 != self.N:
                while True:
                    m += 1
                    if self[m][cube.y] != None:
                        if self[m][cube.y].nbr != cube.nbr: m -= 1
                        break
                    elif m + 1 == self.N: break
            return abs(cube.x - m)

    def move_cube(self, cube, ind, check=False):
        m, change = self.getNumberMove(cube, ind), False
        if ind == "UP":
            if m != 0 and not check: self.score += cube.nbr*2 if self[cube.x][cube.y-m] != None else 0
            if m != 0: new_cube = Cube(cube.x, cube.y-m, cube.nbr*2 if self[cube.x][cube.y-m] != None else cube.nbr)
            else: new_cube = cube ; change = True
            if not check: self[cube.x][cube.y] = None ; self[cube.x][cube.y-m] = new_cube
        elif ind == "DOWN":
            if m != 0 and not check: self.score += cube.nbr*2 if self[cube.x][cube.y+m] != None else 0
            if m != 0: new_cube = Cube(cube.x, cube.y+m, cube.nbr*2 if self[cube.x][cube.y+m] != None else cube.nbr)
            else: new_cube = cube ; change = True
            if not check: self[cube.x][cube.y] = None ; self[cube.x][cube.y+m] = new_cube
        elif ind == "LEFT":
            if m != 0 and not check: self.score += cube.nbr*2 if self[cube.x-m][cube.y] != None else 0
            if m != 0: new_cube = Cube(cube.x-m, cube.y, cube.nbr*2 if self[cube.x-m][cube.y] != None else cube.nbr)
            else: new_cube = cube ; change = True
            if not check: self[cube.x][cube.y] = None ; self[cube.x-m][cube.y] = new_cube
        elif ind == "RIGHT":
            if m != 0 and not check: self.score += cube.nbr*2 if self[cube.x+m][cube.y] != None else 0
            if m != 0: new_cube = Cube(cube.x+m, cube.y, cube.nbr*2 if self[cube.x+m][cube.y] != None else cube.nbr)
            else: new_cube = cube ; change = True
            if not check: self[cube.x][cube.y] = None ; self[cube.x+m][cube.y] = new_cube
        return change

    def move_cubes(self, ind):
        if ind == "UP" or ind == "LEFT":
            for cube in self: self.move_cube(cube, ind) if cube != None else None
        elif ind == "DOWN" or ind == "RIGHT":
            for x in range(self.N-1, -1, -1):
                for y in range(self.N-1, -1, -1): self.move_cube(self[x][y], ind) if self[x][y] != None else None

    def draw_cubes(self, pos_x, pos_y, taille, espace):
        fill_rect(pos_x, pos_y, self.N*(taille+espace)-espace, self.N*(taille+espace)-espace, Screen.palette["Background"])
        for cube in self: cube.draw(pos_x, pos_y, taille, espace) if cube != None else None

    def getNoneAndReplace(self):
        if len(self) != self.N*self.N: new_cube = choice([Cube(x,y,2 if random() <= 0.85 else 4) for y in range(self.N) for x in range(self.N) if self[x][y] is None]) ; self[new_cube.x][new_cube.y] = new_cube

    def check(self):
        if 2048 in [cube.nbr for cube in self if cube != None]: return "WIN"
        elif sum([sum([1 for ind in ["UP", "DOWN", "LEFT", "RIGHT"] if self.move_cube(cube, ind, True)]) for cube in self if cube != None]) / 4 == self.N * self.N: return "LOSE"

class Curseur:
    def __init__(self, *args, default=""): self.args, self.sens, self.default = args, "R", default
    def __next__(self): self.N += 1 if self.sens == "R" else -1 ; self.check() ; self.curs = self.args[self.N] ; return self.curs
    def __iter__(self): self.curs, self.N = self.default, self.args.index(self.default) if self.default != ""  else -1 ; return self
    def check(self):
        if self.N > len(self.args)-1: self.N = 0
        elif self.N < 0: self.N = len(self.args)-1

class GUI:
    nbr_cubes, speed, color_mode = 4, 0.2, "light"
    @staticmethod
    def clean(): fill_rect(0, 0, 320, 222, Screen.palette["Background"])
    class Menu:
        @staticmethod
        def draw():
            def draw_curseur(curseur):
                if curseur == "start":
                    draw_string("  settings        ", 92, 130, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                    draw_string("  graphics        ", 92, 160, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                    draw_string(">  start  <", 105, 100, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                elif curseur == "settings":
                    draw_string("  start        ", 105, 100, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                    draw_string("  graphics        ", 92, 160, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                    draw_string(">  settings  <", 92, 130, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                elif curseur == "graphics":
                    draw_string("  start        ", 105, 100, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                    draw_string("  settings        ", 92, 130, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                    draw_string(">  graphics  <", 92, 160, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            GUI.clean() ; GUI.Menu.C = iter(Curseur("start", "settings", "graphics"))
            draw_string("Game 2048", 105, 50, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            draw_string("  start  ", 105, 100, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            draw_string("  settings  ", 92, 130, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            draw_string("  graphics  ", 92, 160, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            while True:
                if keydown(KEY_UP): GUI.Menu.C.sens = "L" ; next(GUI.Menu.C) ; draw_curseur(GUI.Menu.C.curs) ; sleep(0.15)
                if keydown(KEY_DOWN): GUI.Menu.C.sens = "R" ; next(GUI.Menu.C) ; draw_curseur(GUI.Menu.C.curs) ; sleep(0.15)
                if keydown(KEY_OK) and GUI.Menu.C.curs == "start": game = GUI.Game(GUI.nbr_cubes) ; game.draw() ; break
                if keydown(KEY_OK) and GUI.Menu.C.curs == "settings": GUI.Setting.draw() ; break
                if keydown(KEY_OK) and GUI.Menu.C.curs == "graphics": GUI.Graph.draw() ; break
    class Game:
        def __init__(self, N=4): self.matrice = Matrice(N)# ; self.matrice[0][0] = Cube(0,0,2048)

        def draw(self, pos_x=100, pos_y=23, taille=48, espace=1):
            def check():
                if self.matrice.check() == "WIN": draw_string("You Win", 162, 2, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                elif self.matrice.check() == "LOSE": draw_string("Game Over", 152, 2, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            def update(ind): self.matrice.move_cubes(ind) ; self.matrice.getNoneAndReplace() ; self.matrice.draw_cubes(pos_x, pos_y, taille, espace) ; sleep(GUI.speed) ; draw_string(str(self.matrice.score),8,80,Screen.palette["PrimaryText"],Screen.palette["SecondaryText"])
            GUI.clean()
            draw_string("Score :",14,50,Screen.palette["PrimaryText"],Screen.palette["SecondaryText"])
            draw_string(str(self.matrice.score),8,80,Screen.palette["PrimaryText"],Screen.palette["SecondaryText"])
            fill_rect(pos_x-2, pos_y-2, self.matrice.N*(taille+espace)+3,self.matrice.N*(taille+espace)+3, Screen.palette["PrimaryColor"])
            fill_rect(pos_x-1, pos_y-1, self.matrice.N*(taille+espace)+1,self.matrice.N*(taille+espace)+1, Screen.palette["Background"])
            self.matrice.draw_cubes(pos_x, pos_y, taille, espace)
            while True:
                if keydown(KEY_UP): check() ; update("UP")
                if keydown(KEY_DOWN): check() ; update("DOWN")
                if keydown(KEY_LEFT): check() ; update("LEFT")
                if keydown(KEY_RIGHT): check() ; update("RIGHT")
                if keydown(KEY_BACKSPACE): GUI.Graph.list_score.append(self.matrice.score) ; GUI.Menu.draw() ; break
    class Setting:
        @staticmethod
        def draw():
            def draw_curseur(curseur):
                if curseur == "speed":
                    draw_string(">  "+str(GUI.speed)+"  <    ", 200, 80, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                    draw_string("    "+GUI.color_mode+"        ", 180, 110, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                elif curseur == "color mode":
                    draw_string("   "+str(GUI.speed)+"       ", 200, 80, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
                    draw_string(">   "+GUI.color_mode+"   <    ", 180, 110, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            def change_color(mode):
                if mode == "light": Screen.palette["Background"] = (255,255,255) ; Screen.palette["PrimaryText"] = (0,200,200) ;Screen.palette["SecondaryText"] = (255,255,255) ; Screen.palette["PrimaryColor"] = (0,0,0)
                elif mode == "dark": Screen.palette["Background"] = (80,80,100) ; Screen.palette["PrimaryText"] = (255,255,255) ; Screen.palette["SecondaryText"] = (80,80,100) ; Screen.palette["PrimaryColor"] = (255,255,255)
            GUI.clean() ; GUI.Setting.C = iter(Curseur("speed", "color mode")) ; GUI.Setting.C_speed = iter(Curseur(0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1, default=GUI.speed)) ; GUI.Setting.C_color_mode = iter(Curseur("light", "dark", default=GUI.color_mode))
            draw_string("Settings", 110, 30, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            draw_string("speed", 25, 80, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            draw_string("color mode", 25, 110, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            draw_string("   "+str(GUI.speed)+"       ", 200, 80, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            draw_string("    "+GUI.color_mode+"        ", 180, 110, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            while True:
                if GUI.Setting.C.curs == "speed" and keydown(KEY_RIGHT): GUI.Setting.C_speed.sens = "R" ; next(GUI.Setting.C_speed) ; GUI.speed = GUI.Setting.C_speed.curs ; draw_curseur(GUI.Setting.C.curs) ; sleep(0.15)
                if GUI.Setting.C.curs == "speed" and keydown(KEY_LEFT): GUI.Setting.C_speed.sens = "L" ; next(GUI.Setting.C_speed) ; GUI.speed = GUI.Setting.C_speed.curs ; draw_curseur(GUI.Setting.C.curs) ; sleep(0.15)
                if GUI.Setting.C.curs == "color mode" and keydown(KEY_RIGHT): GUI.Setting.C_color_mode.sens = "R" ; next(GUI.Setting.C_color_mode) ; GUI.color_mode = GUI.Setting.C_color_mode.curs ; draw_curseur(GUI.Setting.C.curs) ; sleep(0.15)
                if GUI.Setting.C.curs == "color mode" and keydown(KEY_LEFT): GUI.Setting.C_color_mode.sens = "L" ; next(GUI.Setting.C_color_mode) ; GUI.color_mode = GUI.Setting.C_color_mode.curs ; draw_curseur(GUI.Setting.C.curs) ; sleep(0.15)
                if keydown(KEY_UP): GUI.Setting.C.sens = "L" ; next(GUI.Setting.C) ; draw_curseur(GUI.Setting.C.curs) ; sleep(0.15)
                if keydown(KEY_DOWN): GUI.Setting.C.sens = "R" ; next(GUI.Setting.C) ; draw_curseur(GUI.Setting.C.curs) ; sleep(0.15)
                if keydown(KEY_BACKSPACE): change_color(GUI.color_mode) ; GUI.clean() ; GUI.Menu.draw() ; break
    class Graph:
        list_score = [0]
        @staticmethod
        def draw():
            GUI.clean()
            draw_string("Graphics", 120, 20, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            draw_string("Maximum Score : "+str(max(GUI.Graph.list_score)), 15, 200, Screen.palette["PrimaryText"], Screen.palette["SecondaryText"])
            fill_rect(10,0,1,222,Screen.palette["PrimaryColor"]);fill_rect(0,196,320,1,Screen.palette["PrimaryColor"])
            for n, bar in enumerate(GUI.Graph.list_score): fill_rect(10+n*(3+2),195,3,round(-bar/103),Screen.palette["PrimaryColor"])
            while True:
                if keydown(KEY_BACKSPACE): GUI.Menu.draw() ; break
    @staticmethod
    def main(): GUI.Menu.draw()

GUI.main()

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.