]> Skullheadx's Git Forge - 2048.git/commitdiff
added everything
authorSkullheadx <704277@pdsb.net>
Tue, 5 Jul 2022 19:11:28 +0000 (15:11 -0400)
committerSkullheadx <704277@pdsb.net>
Tue, 5 Jul 2022 19:11:28 +0000 (15:11 -0400)
copy paste from computer

17 files changed:
2048.exe [new file with mode: 0644]
2048.py [new file with mode: 0644]
assets/2048_logo.png [new file with mode: 0644]
assets/2048_logo.svg.ico [new file with mode: 0644]
assets/2048_logo.svg.png [new file with mode: 0644]
assets/2048_win.png [new file with mode: 0644]
assets/Screenshot 2022-06-13 212716.png [new file with mode: 0644]
assets/Screenshot 2022-06-14 154322.png [new file with mode: 0644]
assets/maxresdefault.jpg [new file with mode: 0644]
assets/tiles.jpg [new file with mode: 0644]
game.py [new file with mode: 0644]
pygametools/__pycache__/label.cpython-39.pyc [new file with mode: 0644]
pygametools/__pycache__/rectangle.cpython-39.pyc [new file with mode: 0644]
pygametools/label.py [new file with mode: 0644]
pygametools/pygame_template.py [new file with mode: 0644]
pygametools/rectangle.py [new file with mode: 0644]
setup.py [new file with mode: 0644]

diff --git a/2048.exe b/2048.exe
new file mode 100644 (file)
index 0000000..6c78437
Binary files /dev/null and b/2048.exe differ
diff --git a/2048.py b/2048.py
new file mode 100644 (file)
index 0000000..7de3b9b
--- /dev/null
+++ b/2048.py
@@ -0,0 +1,27 @@
+from setup import *
+from game import Game
+
+# Set the dimensions of the screen
+screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
+
+scene = Game()
+
+delta = 1000 // fps
+
+# Main loop
+is_running = True
+while is_running:
+
+    # Check if the window is closed
+    if pygame.event.peek(pygame.QUIT):
+        is_running = False
+
+    scene.update(delta)
+    scene.draw(screen)
+
+    # Update the window and find delta
+    pygame.display.update()
+    delta = clock.tick(fps)
+
+# Exit pygame
+pygame.quit()
diff --git a/assets/2048_logo.png b/assets/2048_logo.png
new file mode 100644 (file)
index 0000000..4eef302
Binary files /dev/null and b/assets/2048_logo.png differ
diff --git a/assets/2048_logo.svg.ico b/assets/2048_logo.svg.ico
new file mode 100644 (file)
index 0000000..21a3257
Binary files /dev/null and b/assets/2048_logo.svg.ico differ
diff --git a/assets/2048_logo.svg.png b/assets/2048_logo.svg.png
new file mode 100644 (file)
index 0000000..1f1294f
Binary files /dev/null and b/assets/2048_logo.svg.png differ
diff --git a/assets/2048_win.png b/assets/2048_win.png
new file mode 100644 (file)
index 0000000..7106ade
Binary files /dev/null and b/assets/2048_win.png differ
diff --git a/assets/Screenshot 2022-06-13 212716.png b/assets/Screenshot 2022-06-13 212716.png
new file mode 100644 (file)
index 0000000..7f8a128
Binary files /dev/null and b/assets/Screenshot 2022-06-13 212716.png differ
diff --git a/assets/Screenshot 2022-06-14 154322.png b/assets/Screenshot 2022-06-14 154322.png
new file mode 100644 (file)
index 0000000..a2add84
Binary files /dev/null and b/assets/Screenshot 2022-06-14 154322.png differ
diff --git a/assets/maxresdefault.jpg b/assets/maxresdefault.jpg
new file mode 100644 (file)
index 0000000..2f25c6b
Binary files /dev/null and b/assets/maxresdefault.jpg differ
diff --git a/assets/tiles.jpg b/assets/tiles.jpg
new file mode 100644 (file)
index 0000000..988f91f
Binary files /dev/null and b/assets/tiles.jpg differ
diff --git a/game.py b/game.py
new file mode 100644 (file)
index 0000000..9608646
--- /dev/null
+++ b/game.py
@@ -0,0 +1,554 @@
+import pygame
+
+from setup import *
+
+
+two = 2  # The value of two. Do. Not. Change. It. Disastrous consequences. You have been warned.
+
+
+class Tile:
+    width = 100
+    height = 100
+    speed = 90 / 16  # since delta is 16 when fps is 60
+    colours = [(238, 230, 219),
+               (236, 224, 200),
+               (239, 178, 124),
+               (241, 152, 102),
+               (243, 125, 99),
+               (244, 96, 66),
+               (236, 205, 122),
+               (237, 203, 103),
+               (236, 200, 90),
+               (231, 194, 88),
+               (232, 189, 78),
+               ]
+
+    def __init__(self, pos):
+        self.position = pygame.Vector2(pos)
+        self.target = self.position
+        self.velocity = pygame.Vector2(0, 0)
+        self.number = two ** random.randint(1, 2)  # the tile can start with 2 or 4 according to wikipedia
+        self.can_merge = True
+        self.colour = self.colours[math.floor(math.log(self.number, two)) - 1]
+        self.label = Label(self.get_rect().center, [[[f"{self.number=}", self]]], 20, Colour.BLACK)
+
+    @staticmethod
+    def get_direction(start, end):
+        out = end - start
+        if out == pygame.Vector2(0, 0):
+            return out
+        return out.normalize()
+
+    def update(self, delta, pos):
+        self.target = pos
+        self.velocity = self.get_direction(self.position, pos) * min((self.target - self.position).length(),
+                                                                     self.speed * delta)  # so that we don't overshoot
+        self.position += self.velocity  # We know that there will be no obstacles in the way
+        self.label.update(delta, x=self.get_rect().centerx, y=self.get_rect().centery)
+        self.colour = self.colours[math.floor(math.log(self.number, two)) - 1]
+
+    def is_good(self):
+        if self.position == self.target:
+            return True
+        return False
+
+    def get_rect(self):
+        return pygame.Rect(self.position + pygame.Vector2(5, 5),
+                           (self.width - 10, self.height - 10))  # to account for the grid outlines
+
+    def draw(self, surf):
+        pygame.draw.rect(surf, self.colour, self.get_rect())
+        self.label.draw(surf)
+
+
+class Grid:
+    rows = 4
+    cols = 4
+    cell_width = 100
+    cell_height = 100
+    width = cell_width * cols
+    height = cell_height * rows
+    offset = pygame.Vector2(SCREEN_WIDTH - width, SCREEN_HEIGHT - height) / 2
+
+    def __init__(self):
+        self.grid = self.new_grid()
+        self.direction = None
+        self.prev_direction = None
+        self.just_pressed = False
+        self.can_move = True
+        for i in range(2):
+            self.add_tile()
+
+    def new_grid(self):
+        return [[None for _ in range(self.cols)] for _ in range(self.rows)]
+
+    def no_legal_move(self):
+        def in_bounds(r, c, grid, row):
+            if (0 <= r <= len(grid) - 1 and
+                    0 <= c <= len(row) - 1):
+                return True
+            return False
+
+        for r, row in enumerate(self.grid):
+            for c, val in enumerate(row):
+                if val is None:
+                    return False
+                elif isinstance(val, Tile):
+                    for i in range(-1, 2):
+                        for j in range(-1, 2):
+                            if (i == 0 or j == 0) and not (
+                                    i == 0 and j == 0):  # so that we get the up, down, left, right no diagonal or center
+                                if (in_bounds(r - i, c - j, self.grid, row) and
+                                        isinstance(self.grid[r - i][c - j], Tile) and
+                                        self.grid[r - i][c - j].number == self.grid[r][c].number):
+                                    return False
+        return True
+
+    def get_empty(self):
+        out = []
+        for r, row in enumerate(self.grid):
+            for c, val in enumerate(row):
+                if val is None:
+                    out.append((r, c))
+        return out
+
+    def get_input(self):
+        pressed = pygame.key.get_pressed()
+        if pressed[pygame.K_UP]:
+            return "UP"
+        elif pressed[pygame.K_DOWN]:
+            return "DOWN"
+        elif pressed[pygame.K_LEFT]:
+            return "LEFT"
+        elif pressed[pygame.K_RIGHT]:
+            return "RIGHT"
+        return None
+
+    def update(self, delta, has_won):
+        self.prev_direction = self.direction
+        self.direction = self.get_input()
+        if self.prev_direction != self.direction and self.direction is not None:
+            self.just_pressed = True
+        else:
+            self.just_pressed = False
+
+        score_this_turn = 0
+        if self.can_move and self.just_pressed and has_won:
+            for i in range(max(self.rows, self.cols)):
+                if self.direction == "UP":
+                    for r, row in enumerate(self.grid):
+                        for c, val in enumerate(row):
+                            if not c - 1 < 0:
+                                if isinstance(val, Tile):
+                                    target = self.grid[r][c - 1]
+                                    if target is None:
+                                        self.grid[r][c - 1] = self.grid[r][c]
+                                        self.grid[r][c] = None
+                                    elif isinstance(target,
+                                                    Tile) and target.number == val.number and target.can_merge and val.can_merge:
+                                        target.number *= two
+                                        score_this_turn += target.number
+                                        target.can_merge = False
+                                        self.grid[r][c] = None
+
+                elif self.direction == "DOWN":
+                    for r, row in enumerate(self.grid):
+                        for c, val in enumerate(row[::-1]):
+                            if not c - 1 < 0:
+                                if isinstance(val, Tile):
+                                    target = self.grid[r][len(self.grid[r]) - c]
+                                    if target is None:
+                                        self.grid[r][len(self.grid[r]) - c] = self.grid[r][len(self.grid[r]) - c - 1]
+                                        self.grid[r][len(self.grid[r]) - c - 1] = None
+                                    elif isinstance(target,
+                                                    Tile) and target.number == val.number and target.can_merge and val.can_merge:
+                                        target.number *= two
+                                        score_this_turn += target.number
+                                        target.can_merge = False
+                                        self.grid[r][len(self.grid[r]) - c - 1] = None
+                elif self.direction == "LEFT":
+                    for r, row in enumerate(self.grid):
+                        for c, val in enumerate(row):
+                            if not r - 1 < 0:
+                                if isinstance(val, Tile):
+                                    target = self.grid[r - 1][c]
+                                    if target is None:
+                                        self.grid[r - 1][c] = self.grid[r][c]
+                                        self.grid[r][c] = None
+                                    elif isinstance(target,
+                                                    Tile) and target.number == val.number and target.can_merge and val.can_merge:
+                                        target.number *= two
+                                        score_this_turn += target.number
+                                        target.can_merge = False
+                                        self.grid[r][c] = None
+                elif self.direction == "RIGHT":
+                    for r, row in enumerate(self.grid[::-1]):
+                        for c, val in enumerate(row):
+                            if not r - 1 < 0:
+                                if isinstance(val, Tile):
+                                    target = self.grid[len(self.grid) - r][c]
+                                    if target is None:
+                                        self.grid[len(self.grid) - r][c] = self.grid[len(self.grid) - r - 1][c]
+                                        self.grid[len(self.grid) - r - 1][c] = None
+                                    elif isinstance(target,
+                                                    Tile) and target.number == val.number and target.can_merge and val.can_merge:
+                                        target.number *= two
+                                        score_this_turn += target.number
+                                        target.can_merge = False
+                                        self.grid[len(self.grid) - r - 1][c] = None
+
+        if self.just_pressed and has_won:
+            self.add_tile()
+
+        for r, row in enumerate(self.grid):
+            for c, val in enumerate(row):
+                if isinstance(val, Tile):
+                    val.can_merge = True
+
+        self.can_move = self.find_can_move(delta)
+        return score_this_turn
+
+    def win_condition(self):
+        for r, row in enumerate(self.grid):
+            for c, val in enumerate(row):
+                if isinstance(val, Tile):
+                    if val.number == two ** 11:  # 2 ^ 11 = 2048
+                        return True
+        return False
+
+    def add_tile(self):
+        if len(self.get_empty()) > 0:
+            r, c = random.choice(self.get_empty())
+            self.grid[r][c] = Tile(self.offset + pygame.Vector2(r * self.cell_width, c * self.cell_height))
+
+    def find_can_move(self, delta):
+        for r, row in enumerate(self.grid):
+            for c, val in enumerate(row):
+                if isinstance(val, Tile):
+                    val.update(delta, self.offset + pygame.Vector2(r * self.cell_width, c * self.cell_height))
+                    if not val.is_good():
+                        return False
+        return True
+
+    def get_rect(self, x=0, y=0, w=1, h=1):
+        return pygame.Rect(self.offset + pygame.Vector2(x, y), (w, h))
+
+    def get_cell_rect(self, x, y, w=1, h=1):
+        return pygame.Rect(self.offset + pygame.Vector2(x, y), (self.cell_width * w, self.cell_height * h))
+
+    def draw(self, surf):
+        for r, row in enumerate(self.grid):
+            for c, val in enumerate(row):
+                pygame.draw.rect(surf, Colour.SQUARE_BG, self.get_cell_rect(r * self.cell_width, c * self.cell_height))
+                pygame.draw.rect(surf, Colour.SQUARE_OUTLINE,
+                                 self.get_cell_rect(r * self.cell_width, c * self.cell_height), 10)
+                if isinstance(val, Tile):
+                    val.draw(surf)
+        pygame.draw.rect(surf, Colour.SQUARE_OUTLINE, self.get_rect(-5, -5, self.width + 10, self.height + 10), 10, 5)
+
+class Effect:
+    speed = 0.15
+
+    def __init__(self, pos, txt):
+        self.label = Label(pos, txt, 15, Colour.BLACK)
+        self.vel_y = 0
+
+    def update(self, delta):
+        self.label.update(delta, y=self.label.position.y + self.vel_y)
+        self.vel_y -= self.speed
+
+    def is_out_of_bounds(self):
+        if self.label.position.y + self.label.height < 0:
+            return True
+        return False
+
+
+    def draw(self, surf):
+        self.label.draw(surf)
+
+class Game:
+    # Background colour
+    background_colour = (250, 248, 239)
+    high_score = 0
+    has_won = False
+
+    def __init__(self):
+        self.win = False
+        self.game_over = False
+        self.grid = Grid()
+        self.win_effect = pygame.Surface((self.grid.width + 10, self.grid.height + 10))
+        self.win_effect_time = 0
+        self.game_title = two ** 11
+        self.title = Label((0, 0), [[[f"{self.game_title=}", self]]], 50, Colour.BLACK, centered_x=False,
+                           centered_y=False)
+        self.score = 0
+        self.score_label = Label((0, 0), [["Score:"], [[f"{self.score=}", self]]], 15, Colour.WHITE, filled=True,
+                                 fill_colour=Colour.SQUARE_OUTLINE)
+        self.high_score_label = Label((0, 0), [["Best:"], [[f"{self.high_score=}", self]]], 15, Colour.WHITE,
+                                      filled=True, fill_colour=Colour.SQUARE_OUTLINE)
+        self.new_game_button = Button((SCREEN_WIDTH / 2, SCREEN_HEIGHT * 8 / 9), [["New Game"]], 20, self.__init__,
+                                      Colour.BLACK, filled=True, fill_colour=Colour.SQUARE_OUTLINE, outlined=True,
+                                      outline_colour=Colour.GRAY, outline_radius=5)
+        self.how_to_play = Label((0, 0),
+                                 [["Join the numbers and get to the ", [f"{self.game_title=}", self], " tile!"]], 15,
+                                 Colour.BLACK, centered_x=False)
+        self.continue_button = Button((SCREEN_WIDTH / 3, SCREEN_HEIGHT * 3 / 5), [["Keep going"]], 20, self.keep_going,
+                                      Colour.BLACK, filled=True, fill_colour=Colour.SQUARE_OUTLINE, outlined=True,
+                                      outline_colour=Colour.GRAY, outline_radius=5)
+        self.replay_button = Button((SCREEN_WIDTH/2, SCREEN_HEIGHT * 3 / 5), [["Try again"]], 20, self.__init__,
+                                    Colour.BLACK, filled=True, fill_colour=Colour.SQUARE_OUTLINE, outlined=True,
+                                    outline_colour=Colour.GRAY, outline_radius=5)
+        self.win_label = Label((SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - self.grid.cell_height / 4), [["You win!"]], 60,
+                               Colour.WHITE)
+        self.lose_label = Label((SCREEN_WIDTH/2, SCREEN_HEIGHT/2 - self.grid.cell_height/4), [["You lose!"]], 60, Colour.WHITE)
+        self.effects = []
+        self.temp = 0
+        self.update(0)
+
+    def keep_going(self):
+        self.has_won = True
+        self.win = False
+
+    def update(self, delta):
+        if self.game_over:
+            self.lose_label.update(delta)
+            self.win_effect.fill(Colour.BLACK)
+            self.win_effect.set_alpha(min(75, (math.floor(255 * self.win_effect_time / 1000))))
+            self.win_effect_time += delta
+            self.replay_button.update(delta, x=SCREEN_WIDTH / 2)
+
+        self.temp = self.grid.update(delta, not self.win)
+        self.score += self.temp
+        if self.temp > 0:
+            self.effects.append(Effect(self.score_label.position, [[f"+{self.temp}"]]))
+        if self.win:
+            self.win_label.update(delta)
+            self.continue_button.update(delta)
+            self.replay_button.update(delta, x=SCREEN_WIDTH * 2 / 3)
+            self.win_effect.fill(Colour.GOLD)
+            self.win_effect.set_alpha(min(75, (math.floor(255 * self.win_effect_time / 10000))))
+            self.win_effect_time += delta
+        self.high_score = max(self.score, self.high_score)
+        if not self.has_won and self.grid.win_condition():
+            self.win = True
+            self.has_won = True
+        if self.grid.no_legal_move():
+            self.game_over = True
+        self.title.update(delta, x=self.grid.offset.x, y=self.grid.offset.y - self.title.height * 1.5)
+        self.high_score_label.update(delta, x=self.grid.offset.x + self.grid.width - self.high_score_label.width / 2,
+                                     y=self.grid.offset.y - self.high_score_label.height * 1.5)
+        self.score_label.update(delta,
+                                x=self.high_score_label.position.x - self.high_score_label.width / 2 - self.score_label.width - Label.horizontal_padding,
+                                y=self.grid.offset.y - self.high_score_label.height * 1.5)
+        self.how_to_play.update(delta, x=self.grid.offset.x, y=self.grid.offset.y - self.how_to_play.height)
+        self.new_game_button.update(delta)
+        for effect in self.effects:
+            effect.update(delta)
+            if effect.is_out_of_bounds():
+                del self.effects[self.effects.index(effect)]
+
+    def draw(self, surf):
+        surf.fill(self.background_colour)
+        self.grid.draw(surf)
+        self.title.draw(surf)
+        self.score_label.draw(surf)
+        self.high_score_label.draw(surf)
+        self.new_game_button.draw(surf)
+        self.how_to_play.draw(surf)
+        for effect in self.effects:
+            effect.draw(surf)
+        if self.win:
+            surf.blit(self.win_effect, self.grid.offset - pygame.Vector2(5, 5))
+            self.win_label.draw(surf)
+            self.continue_button.draw(surf)
+            self.replay_button.draw(surf)
+        elif self.game_over:
+            surf.blit(self.win_effect, self.grid.offset - pygame.Vector2(5,5))
+            self.lose_label.draw(surf)
+            self.replay_button.draw(surf)
+
+
+"""
+class Tile:
+    width = SCREEN_WIDTH / 2 / 4
+    height = SCREEN_WIDTH / 2 / 4
+    gravity_force = 0.1
+
+    def __init__(self, pos, collide_list, number=2 ** random.randint(1,2)):
+        self.position = pygame.Vector2(pos)
+        self.velocity = pygame.Vector2(0, 0)
+        self.up_direction = pygame.Vector2(0, 0)
+        self.collide_list = collide_list
+        self.collide_list.append(self)
+        self.can_move = True
+        self.number = 2
+        self.center = self.get_center()
+        self.label = Label(self.center, [[[f"{self.number=}", self]]], 20, Colour.BLACK)
+
+    def get_center(self):
+        return self.position + pygame.Vector2(self.width/2, self.height/2)
+
+
+    def get_collision_rect(self):
+        return pygame.Rect(self.position, (self.width, self.height))
+
+    def get_input(self):
+        pressed = pygame.key.get_pressed()
+        if pressed[pygame.K_LEFT]:
+            self.up_direction = pygame.Vector2(-1, 0)
+            return True
+        elif pressed[pygame.K_RIGHT]:
+            self.up_direction = pygame.Vector2(1, 0)
+            return True
+        elif pressed[pygame.K_UP]:
+            self.up_direction = pygame.Vector2(0, -1)
+            return True
+        elif pressed[pygame.K_DOWN]:
+            self.up_direction = pygame.Vector2(0, 1)
+            return True
+
+    def collided(self, thing): # called when the tile collides
+        self.can_move = True
+        if isinstance(thing, Tile):
+            if thing.number == self.number:
+                thing.number *= 2
+            if self in self.collide_list:
+                del self.collide_list[self.collide_list.index(self)]
+
+    def update(self, delta, can_move):
+        if can_move:
+            self.get_input()
+
+        self.position, self.velocity = self.move_and_collide(delta, self.position, self.velocity, self.up_direction)
+        self.center = self.get_center()
+        self.label.update(delta, x=self.center.x, y=self.center.y)
+
+    def move_and_collide(self, delta, position, velocity, up_direction=pygame.Vector2(0, 0)):
+        pos = position
+        vel = velocity
+        vel += up_direction * delta * self.gravity_force
+
+        pos.x += vel.x * delta
+        for thing in self.collide_list:
+            if thing == self:
+                continue
+            if self.get_collision_rect().colliderect(thing.get_collision_rect()):
+                if vel.x > 0:
+                    pos.x = thing.position.x - self.width
+                else:
+                    pos.x = thing.position.x + thing.width
+                vel.x = 0
+                self.collided(thing)
+                break
+
+        pos.y += vel.y * delta
+        for thing in self.collide_list:
+            if thing == self:
+                continue
+            if self.get_collision_rect().colliderect(thing.get_collision_rect()):
+                if vel.y > 0:
+                    pos.y = thing.position.y - self.height
+                else:
+                    pos.y = thing.position.y + thing.height
+                vel.y = 0
+                self.collided(thing)
+                break
+
+        return pos, vel
+
+    def draw(self, surf):
+        pygame.draw.rect(surf, Colour.RED, self.get_collision_rect())
+        self.label.draw(surf)
+
+
+class Wall:
+
+    def __init__(self, pos, collide_list, is_vertical=True, face_right=True, visible=True):
+        self.position = pygame.Vector2(pos)
+        self.start_pos = pygame.Vector2(pos)
+        self.collide_list = collide_list
+        self.collide_list.append(self)
+
+        self.visible = visible
+
+        self.width = SCREEN_WIDTH / 2
+        self.height = SCREEN_HEIGHT / 2
+
+        if is_vertical:
+            self.end_pos = self.position + pygame.Vector2(0, self.height)
+            if not face_right:
+                self.position.x -= self.width
+        else:
+            self.end_pos = self.position + pygame.Vector2(self.width, 0)
+            if not face_right:
+                self.position.y -= self.height
+
+        self.end_point = pygame.Vector2(self.width, self.height)
+
+    def get_collision_rect(self):
+        return pygame.Rect(self.position, self.end_point)
+
+    def update(self, delta):
+        pass
+
+    def draw(self, surf):
+        # pygame.draw.rect(surf, Colour.BLACK, self.get_collision_rect())
+        if self.visible:
+            pygame.draw.line(surf, Colour.BLACK, self.start_pos, self.end_pos, 10)
+
+
+class Game:
+
+    def __init__(self):
+        self.collision_list = []
+        self.walls = [
+            Wall((SCREEN_WIDTH / 4, SCREEN_HEIGHT * 3 / 4), self.collision_list, False, True),
+            Wall((SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4), self.collision_list, False, False),
+            Wall((SCREEN_WIDTH * 3 / 4, SCREEN_HEIGHT / 4), self.collision_list, True, True),
+            Wall((SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4), self.collision_list, True, False),
+            Wall((SCREEN_WIDTH * 3 / 4, SCREEN_HEIGHT * 3 / 4), self.collision_list, False, True, False),
+            Wall((SCREEN_WIDTH * 3 / 4, SCREEN_HEIGHT / 4), self.collision_list, False, False, False),
+            Wall((SCREEN_WIDTH/-4, SCREEN_HEIGHT / 4), self.collision_list, False, False, False),
+            Wall((SCREEN_WIDTH / -4, SCREEN_HEIGHT * 5 /4), self.collision_list, False, False, False)
+        ]
+        self.can_move = True
+        self.tiles = [
+            Tile((SCREEN_WIDTH * 2 / 8, SCREEN_HEIGHT * 2 / 8), self.collision_list),
+            Tile((SCREEN_WIDTH * 3 / 8, SCREEN_HEIGHT * 3 / 8), self.collision_list),
+            Tile((SCREEN_WIDTH * 4 / 8, SCREEN_HEIGHT * 4 / 8), self.collision_list),
+            Tile((SCREEN_WIDTH * 5 / 8, SCREEN_HEIGHT * 5 / 8), self.collision_list),
+            ]
+        self.title = Label((SCREEN_WIDTH/2, SCREEN_HEIGHT/8), ["2048"], 50, Colour.ORANGE)
+        print(self.collision_list)
+
+
+    def update(self, delta):
+
+        if self.check_can_move():
+            self.can_move = True
+        else:
+            self.can_move = False
+
+        for tile in self.tiles:
+            tile.update(delta, self.can_move)
+        print(self.collision_list)
+
+        for wall in self.walls:
+            wall.update(delta)
+
+        self.title.update(delta)
+
+    def check_can_move(self):
+        for tile in self.tiles:
+            if not tile.can_move:
+                return False
+        return True
+    def draw(self, surf):
+        surf.fill(background_colour)
+
+        for thing in self.collision_list:
+            thing.draw(surf)
+
+        self.title.draw(surf)
+
+"""
diff --git a/pygametools/__pycache__/label.cpython-39.pyc b/pygametools/__pycache__/label.cpython-39.pyc
new file mode 100644 (file)
index 0000000..3ad6894
Binary files /dev/null and b/pygametools/__pycache__/label.cpython-39.pyc differ
diff --git a/pygametools/__pycache__/rectangle.cpython-39.pyc b/pygametools/__pycache__/rectangle.cpython-39.pyc
new file mode 100644 (file)
index 0000000..408113b
Binary files /dev/null and b/pygametools/__pycache__/rectangle.cpython-39.pyc differ
diff --git a/pygametools/label.py b/pygametools/label.py
new file mode 100644 (file)
index 0000000..84c5e06
--- /dev/null
@@ -0,0 +1,290 @@
+import pygame
+from pygametools.rectangle import RectangleLite, Rectangle
+
+
+class LabelLite:
+    horizontal_padding = 5
+    vertical_padding = 5
+    outline_thickness = 2
+
+    def __init__(self, pos, text, font_size, text_colour=(0, 0, 0), antialias=True, centered_x=True, centered_y=True,
+                 filled=False, fill_colour=(150, 150, 150), outlined=False, outline_colour=(0, 0, 0), outline_radius=2,
+                 font_file="C:/Users/admon/PycharmProjects/coding_club/assets/fonts/montserrat/MontserratRegular-BWBEl.ttf"
+                 ):
+        self.position = pygame.Vector2(pos)
+
+        self.centered_x = centered_x
+        self.centered_y = centered_y
+        self.centering = pygame.Vector2(0, 0)
+
+        self.font_file = font_file
+        self.font = pygame.font.Font(self.font_file, font_size)
+        self.text_colour = text_colour
+        self.antialias = antialias
+        self.raw_text = text
+        self.text, self.width, self.height = self.create_text(self.raw_text)
+
+        self.rect = RectangleLite(pos, self.width, self.height, centered_x, centered_y, filled, fill_colour, outlined,
+                                  outline_colour, self.outline_thickness, outline_radius)
+
+        self.filled = filled
+        self.fill_colour = fill_colour
+
+        self.outlined = outlined
+        self.outline_colour = outline_colour
+
+    def create_text(self, text):
+        out = []
+        max_width = 0
+        max_height = 0
+
+        for line in text:
+            text_surface = self.font.render(line, self.antialias, self.text_colour)
+            width = text_surface.get_width()
+            height = text_surface.get_height()
+            out.append((text_surface, width, height))
+            max_width = max(max_width, width)
+            max_height += height
+        return out, max_width, max_height
+
+    def update(self, delta, new_text=None, x=None, y=None, width=None, height=None, colour=None, font_size=None):
+        self.text, self.width, self.height = self.create_text(self.raw_text)
+
+        if new_text is not None:
+            self.text, self.width, self.height = self.create_text(new_text)
+        if font_size is not None:
+            self.font = pygame.font.Font(self.font_file, font_size)
+            if new_text is not None:
+                self.text, self.width, self.height = self.create_text(new_text)
+            else:
+                self.text, self.width, self.height = self.create_text(self.raw_text)
+
+        if x is not None:
+            self.position.x = x
+        if y is not None:
+            self.position.y = y
+        if width is not None:
+            self.width = width
+        if height is not None:
+            self.height = height
+        if colour is not None:
+            self.fill_colour = colour
+        if self.centered_x:
+            self.centering.x = self.width / 2
+        if self.centered_y:
+            self.centering.y = self.height / 2
+
+        self.rect.update(delta, self.position.x, self.position.y, self.width, self.height, self.fill_colour)
+
+    def draw(self, surface):
+
+        self.rect.draw(surface)
+
+        prev_height = 0
+        for line in self.text:
+            text_surface, width, height = line
+            center = pygame.Vector2(0, 0)
+            if self.centered_x:
+                center.x = width / 2
+            if self.centered_y:
+                center.y = height / 2
+            surface.blit(text_surface, self.position - center + pygame.Vector2(0, prev_height))
+            prev_height += height
+
+
+class Label:
+    horizontal_padding = 5
+    vertical_padding = 5
+    outline_thickness = 4
+
+    def __init__(self, pos, text, font_size, text_colour=(0, 0, 0), antialias=True, centered_x=True, centered_y=True,
+                 filled=False, fill_colour=(150, 150, 150), outlined=False, outline_colour=(0, 0, 0), outline_radius=2,
+                 font_file="C:/Users/admon/PycharmProjects/coding_club/assets/fonts/montserrat/MontserratRegular-BWBEl.ttf",
+                 path=None):
+
+        if path is None:
+            path = globals()
+        self.position = pygame.Vector2(pos)
+
+        self.centered_x = centered_x
+        self.centered_y = centered_y
+        self.centering = pygame.Vector2(0, 0)
+
+        self.font_file = font_file
+        self.font = pygame.font.Font(self.font_file, font_size)
+        self.text_colour = text_colour
+        self.antialias = antialias
+        self.raw_text = text
+
+        self.path = path
+        self.text, self.width, self.height = self.create_text(self.raw_text, self.path)
+
+        self.rect = RectangleLite(pos, self.width, self.height, centered_x, centered_y, filled, fill_colour, outlined,
+                                  outline_colour, self.outline_thickness, outline_radius)
+
+        self.filled = filled
+        self.fill_colour = fill_colour
+
+        self.outlined = outlined
+        self.outline_colour = outline_colour
+
+    def create_text(self, text, path):
+        """
+        [["string ", [f"{var=}", path]], ["string2"]]
+        string var
+        string2
+        """
+        out = []
+        max_width = 0
+        max_height = 0
+
+        for l in text:
+            line = ""
+            for thing in l:
+                if isinstance(thing, list):
+                    if len(thing) == 1:
+                        value = thing[0].split('=')[0]
+                        line += str(path[value])
+                    else:
+                        val, path = thing
+                        value = val.split('=')[0]
+                        if "self." in value:
+                            ind = value.index("self.")
+                            value = value[ind + 5:]
+                        line += eval("str(path.%s)" % value)
+
+                else:
+                    line += thing
+
+            text_surface = self.font.render(line, self.antialias, self.text_colour)
+            width = text_surface.get_width()
+            height = text_surface.get_height()
+            out.append((text_surface, width, height))
+            max_width = max(max_width, width)
+            max_height += height
+        return out, max_width, max_height
+
+    def update(self, delta, new_text=None, x=None, y=None, width=None, height=None, colour=None, font_size=None):
+        self.text, self.width, self.height = self.create_text(self.raw_text, self.path)
+
+        if new_text is not None:
+            self.text, self.width, self.height = self.create_text(new_text, self.path)
+        if font_size is not None:
+            self.font = pygame.font.Font(self.font_file, font_size)
+            if new_text is not None:
+                self.text, self.width, self.height = self.create_text(new_text, self.path)
+            else:
+                self.text, self.width, self.height = self.create_text(self.raw_text, self.path)
+
+        if x is not None:
+            self.position.x = x
+        if y is not None:
+            self.position.y = y
+        if width is not None:
+            self.width = width
+        if height is not None:
+            self.height = height
+        if colour is not None:
+            self.fill_colour = colour
+        if self.centered_x:
+            self.centering.x = self.width / 2
+        if self.centered_y:
+            self.centering.y = self.height / 2
+
+        self.rect.update(delta, self.position.x, self.position.y, self.width, self.height, self.fill_colour)
+
+    def draw(self, surface):
+
+        self.rect.draw(surface)
+
+        prev_height = 0
+        for line in self.text:
+            text_surface, width, height = line
+            center = pygame.Vector2(0, 0)
+            if self.centered_x:
+                center.x = self.width/2 - width/2
+            # if self.centered_y:
+            #     center.y = height / 2
+            surface.blit(text_surface, self.position + center - self.centering + pygame.Vector2(0, prev_height))
+            prev_height += height
+
+
+class ButtonLite(LabelLite):
+
+    def __init__(self, pos, text, font_size, func, text_colour=(0, 0, 0), antialias=True, centered_x=True,
+                 centered_y=True,
+                 filled=False, fill_colour=(150, 150, 150), outlined=False, outline_colour=(0, 0, 0), outline_radius=2,
+                 font_file="C:/Users/admon/PycharmProjects/coding_club/assets/fonts/montserrat/MontserratRegular-BWBEl.ttf"):
+        super().__init__(pos, text, font_size, text_colour, antialias, centered_x, centered_y,
+                         filled, fill_colour, outlined, outline_colour, outline_radius,
+                         font_file)
+        self.func = func
+
+    def is_touching_mouse_pointer(self):
+        mouse_x, mouse_y = pygame.mouse.get_pos()
+        if (
+                self.position.x - self.centering.x - self.horizontal_padding <= mouse_x <= self.position.x + self.width - self.centering.x + self.horizontal_padding
+                and self.position.y - self.centering.y - self.vertical_padding <= mouse_y <= self.position.y + self.height - self.centering.y + self.vertical_padding):
+            return True
+        return False
+
+    def lighten(self):
+        self.fill_colour = (100, 100, 100)
+
+    def darken(self):
+        self.fill_colour = (150, 150, 150)
+
+    def update(self, delta, new_text=None, x=None, y=None, width=None, height=None, colour=None, font_size=None):
+        super().update(delta, new_text, x, y, width, height, colour, font_size)
+
+        if self.is_touching_mouse_pointer():
+            self.lighten()
+            if pygame.mouse.get_pressed(3)[0]:
+                self.run_function()
+        else:
+            self.darken()
+
+    def run_function(self):
+        return self.func()
+
+
+class Button(Label):
+
+    def __init__(self, pos, text, font_size, func, text_colour=(0, 0, 0), antialias=True, centered_x=True,
+                 centered_y=True, filled=False, fill_colour=(150, 150, 150), outlined=False, outline_colour=(0, 0, 0), outline_radius=2,
+                 font_file="C:/Users/admon/PycharmProjects/coding_club/assets/fonts/montserrat/MontserratRegular-BWBEl.ttf", path=None, cooldown=100):
+        super().__init__(pos, text, font_size, text_colour, antialias, centered_x, centered_y,
+                         filled, fill_colour, outlined, outline_colour, outline_radius,
+                         font_file, path)
+        self.func = func
+        self.cooldown = cooldown
+
+    def is_touching_mouse_pointer(self):
+        mouse_x, mouse_y = pygame.mouse.get_pos()
+        if (
+                self.position.x - self.centering.x - self.horizontal_padding <= mouse_x <= self.position.x + self.width - self.centering.x + self.horizontal_padding
+                and self.position.y - self.centering.y - self.vertical_padding <= mouse_y <= self.position.y + self.height - self.centering.y + self.vertical_padding):
+            return True
+        return False
+
+    def lighten(self):
+        self.fill_colour = (100, 100, 100)
+
+    def darken(self):
+        self.fill_colour = (150, 150, 150)
+
+    def update(self, delta, new_text=None, x=None, y=None, width=None, height=None, colour=None, font_size=None):
+        super().update(delta, new_text, x, y, width, height, colour, font_size)
+
+        if self.is_touching_mouse_pointer():
+            self.lighten()
+            if self.cooldown == 0 and pygame.mouse.get_pressed(3)[0]:
+                self.run_function()
+        else:
+            self.darken()
+
+        self.cooldown -= delta
+        self.cooldown = max(self.cooldown, 0)
+
+    def run_function(self):
+        return self.func()
diff --git a/pygametools/pygame_template.py b/pygametools/pygame_template.py
new file mode 100644 (file)
index 0000000..9e3780c
--- /dev/null
@@ -0,0 +1,41 @@
+import pygame
+from pygametools.label import Label, Button
+
+# Initialise pygame
+pygame.init()
+
+# Set the dimensions of the screen
+SCREEN_HEIGHT = 720
+SCREEN_WIDTH = 720
+screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
+
+# Set the name and icon of the window
+# pygame.display.set_caption("pygame")
+# icon = pygame.image.load("filename")
+# icon = pygame.transform.scale(icon, (32, 32))
+# pygame.display.set_icon(icon)
+
+# Find out delta and create clock
+clock = pygame.time.Clock()
+fps = 60
+delta = 1000 // fps
+
+# Background colour
+background_colour = (255, 255, 255)
+
+# Main loop
+is_running = True
+while is_running:
+
+    screen.fill(background_colour)
+
+    # Check if the window is closed
+    if pygame.event.peek(pygame.QUIT):
+        is_running = False
+
+    # Update the window and find delta
+    pygame.display.update()
+    delta = clock.tick(fps)
+
+# Exit pygame
+pygame.quit()
diff --git a/pygametools/rectangle.py b/pygametools/rectangle.py
new file mode 100644 (file)
index 0000000..f70a599
--- /dev/null
@@ -0,0 +1,107 @@
+import pygame
+
+
+class RectangleLite:
+    horizontal_padding = 5
+    vertical_padding = 5
+
+    def __init__(self, pos, width, height, centered_x=True, centered_y=True,
+                 filled=True, fill_colour=(255, 255, 255, 255),
+                 outlined=True, outline_colour=(0, 0, 0, 255), outline_thickness=2, outline_radius=0):
+        x, y = pos
+        self.position = pygame.Vector2(x, y)
+        self.width = width
+        self.height = height
+
+        self.centered_x = centered_x
+        self.centered_y = centered_y
+        self.centering = pygame.Vector2(0, 0)
+
+        self.filled = filled
+        self.fill_colour = fill_colour
+
+        self.outlined = outlined
+        self.outline_colour = outline_colour
+        self.outline_thickness = outline_thickness
+        self.outline_radius = outline_radius
+
+    def update(self, delta, x=None, y=None, width=None, height=None, fill_colour=None):
+        if x is not None:
+            self.position.x = x
+        if y is not None:
+            self.position.y = y
+        if width is not None:
+            self.width = width
+        if height is not None:
+            self.height = height
+        if fill_colour is not None:
+            self.fill_colour = fill_colour
+
+        if self.centered_x:
+            self.centering.x = self.width / 2
+        if self.centered_y:
+            self.centering.y = self.height / 2
+
+    def get_collision_rect(self):
+        return pygame.Rect(
+            self.position - pygame.Vector2(self.horizontal_padding, self.vertical_padding) - self.centering,
+            (self.width + 2 * self.horizontal_padding, self.height + 2 * self.vertical_padding))
+
+    def draw(self, surface):
+        r = self.get_collision_rect()
+        if self.filled:
+            pygame.draw.rect(surface, self.fill_colour, r,border_radius=self.outline_radius)
+        if self.outlined:
+            pygame.draw.rect(surface, self.outline_colour, r, self.outline_thickness, self.outline_radius)
+
+
+class Rectangle(RectangleLite):
+
+    def __init__(self, pos, width, height, centered_x=True, centered_y=True,
+                 filled=True, fill_colour=(255, 255, 255, 255),
+                 outlined=True, outline_colour=(0, 0, 0, 255), outline_thickness=2, outline_radius=0,
+                 horizontal_padding=12, vertical_padding=12):
+
+        super().__init__(pos, width, height, centered_x, centered_y, filled, fill_colour,
+                         outlined, outline_colour, outline_thickness, outline_radius)
+
+        self.horizontal_padding = horizontal_padding
+        self.vertical_padding = vertical_padding
+
+    def update(self, delta, x=None, y=None, dx=0, dy=0, width=None, height=None, fill_colour=None,
+               centered_x=None, centered_y=None, outlined=None, outline_colour=None, outline_thickness=None,
+               outline_radius=None):
+        if x is not None:
+            self.position.x = x
+        if y is not None:
+            self.position.y = y
+        self.position += delta * pygame.Vector2(dx, dy)
+        if width is not None:
+            self.width = width
+        if height is not None:
+            self.height = height
+        if fill_colour is not None:
+            self.fill_colour = fill_colour
+        if centered_x is not None:
+            self.centered_x = centered_x
+        if centered_y is not None:
+            self.centered_y = centered_y
+        if outlined is not None:
+            self.outlined = outlined
+        if self.outlined:
+            if outline_colour is not None:
+                self.outline_colour = outline_colour
+            if outline_thickness is not None:
+                self.outline_thickness = outline_thickness
+            if outline_radius is not None:
+                self.outline_radius = outline_radius
+
+        if self.centered_x:
+            self.centering.x = self.width / 2
+        if self.centered_y:
+            self.centering.y = self.height / 2
+
+    def draw(self, surface):
+        s = pygame.Surface((surface.get_width(), surface.get_height()), pygame.SRCALPHA)
+        super().draw(s)
+        surface.blit(s, (0, 0))
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..04eac36
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,39 @@
+import pygame
+import random
+import math
+from pygametools.label import Label, Button
+
+
+# Initialise pygame
+pygame.init()
+
+# Set the dimensions of the screen
+SCREEN_WIDTH, SCREEN_HEIGHT = 640, 640
+
+# Set the name and icon of the window
+pygame.display.set_caption("2048")
+icon = pygame.image.load("assets/2048_logo.svg.png")
+icon = pygame.transform.scale(icon, (32, 32))
+pygame.display.set_icon(icon)
+
+# Find out delta and create clock
+clock = pygame.time.Clock()
+fps = 60
+
+
+class Colour:
+    BLACK = (0, 0, 0)
+    WHITE = (255, 255, 255)
+    RED = (255, 0, 0)
+    YELLOW = (255, 255, 0)
+    LIGHT_PINK = (255, 209, 223)
+    LIGHT_BLUE = (213, 209, 255)
+    LIGHT_YELLOW = (255, 250, 209)
+    ORANGE = (255, 173, 51)
+    OFF_RED = (255, 69, 69)
+    PURPLE = (202, 69, 255)
+    DARK_BLUE = (57, 26, 135)
+    GOLD = (255,215,0)
+    GRAY = (125, 125, 125)
+    SQUARE_BG = (199, 187, 171)
+    SQUARE_OUTLINE = (182, 163, 148)