diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90f8fbb --- /dev/null +++ b/.gitignore @@ -0,0 +1,176 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python \ No newline at end of file diff --git a/game_plat.py b/game_plat.py index 6ec8695..12a46b5 100644 --- a/game_plat.py +++ b/game_plat.py @@ -3,15 +3,16 @@ import math import numpy as np import random from utils.cv_marker import cap_and_mark -from utils.stack_exe import Stackbot +# from utils.stack_exe import Stackbot import cv2 +import sys pygame.init() WIDTH, HEIGHT = 800, 600 screen = pygame.display.set_mode((WIDTH, HEIGHT)) -pygame.display.set_caption("积木塔仿真") +pygame.display.set_caption("Stack Simulation") WHITE = (255, 255, 255) BLACK = (0, 0, 0) @@ -28,6 +29,14 @@ cap.set(6,cv2.VideoWriter.fourcc('M','J','P','G')) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080) +# 定义屏幕尺寸 +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 + +# 定义按钮尺寸 +BUTTON_WIDTH = 200 +BUTTON_HEIGHT = 200 + class Block: def __init__(self, x, y, width, height, angle, layer): self.x = x @@ -62,27 +71,34 @@ class Block: class Tower: - def __init__(self): + def __init__(self, blueprint, tower_image=None): self.blocks = [] self.current_layer = 0 - self.layer_centroids = [] # 存储每层的重心 - self.stability_threshold = 10 # 稳定性阈值(像素) - self.rotation_angle = 15 # 预设每层旋转角度 - self.angle_tolerance = 5 # 允许的角度偏移 - self.position_tolerance = 10 # 允许的位置偏移 + self.layer_centroids = [] + self.stability_threshold = 10 + self.rotation_angle = 15 + self.angle_tolerance = 5 + self.position_tolerance = 10 self.sim_to_robot_matrix = np.array([ - [1, 0, 0, -300], # 假设的转换矩阵,需要根据实际情况调整 + [1, 0, 0, -300], [0, -1, 0, -200], [0, 0, 1, 0], [0, 0, 0, 1] ]) self.robot_to_sim_matrix = np.linalg.inv(self.sim_to_robot_matrix) - self.stack_bot = Stackbot() - self.is_upper = False + self.blueprint = blueprint + self.center_x, self.center_y = 300, 300 # 定义塔的中心点 + self.ideal_positions = self.get_ideal_positions() + self.position_tolerance = 20 + self.angle_tolerance = 15 + + self.tower_image = None + if tower_image: + self.tower_image = pygame.transform.scale(tower_image, (150, 150)) # 调整图片大小 def sim_to_robot_coords(self, x, y, angle): sim_coords = np.array([x, y, 0, 1]) @@ -106,7 +122,7 @@ class Tower: angle (float): 机器人的角度 返回值: - bool:如果方块添加成功,返回 True,否则返回 False + bool:如果方块添加成,返回 True,否则返回 False 注意:这个函数需要先调用 robot_to_sim_coords 来将机器人坐标转换为模拟坐标,并且需要一个名为 Block 的类来创建方块对象 """ @@ -121,21 +137,27 @@ class Tower: def add_block(self, block): - if self.current_layer == 5: - self.is_upper = True - print("is upper") + self.add_block_from_sim(block) + return True + # if self.current_layer == 5: + # self.is_upper = True + # print("is upper") - if not self.check_interference(block): - self.blocks.append(block) - self.stack_bot.pick_stack() - robo_xyz = self.sim_to_robot_coords(block.x, block.y, block.angle) - print(block.x, block.y, block.angle) - if self.is_upper: - self.stack_bot.place_stack(robo_xyz[0], robo_xyz[1], robo_xyz[2], self.current_layer - 5) - else: - self.stack_bot.place_stack(robo_xyz[0], robo_xyz[1], robo_xyz[2], self.current_layer) - return True - return False + # if not self.check_interference(block): + # self.blocks.append(block) + # self.stack_bot.pick_stack() + # robo_xyz = self.sim_to_robot_coords(block.x, block.y, block.angle) + # print(block.x, block.y, block.angle) + # if self.is_upper: + # self.stack_bot.place_stack(robo_xyz[0], robo_xyz[1], robo_xyz[2], self.current_layer - 5) + # else: + # self.stack_bot.place_stack(robo_xyz[0], robo_xyz[1], robo_xyz[2], self.current_layer) + # return True + # return False + + def add_block_from_sim(self, block): + self.blocks.append(block) + return True def draw(self, surface): for block in self.blocks: @@ -252,49 +274,84 @@ class Tower: self.layer_centroids.append(centroid) self.current_layer += 1 + def get_ideal_positions(self): + # 将相对坐标转换为绝对坐标 + absolute_positions = [] + for layer in self.blueprint: + layer_positions = [] + for rel_x, rel_y, angle in layer: + abs_x = self.center_x + rel_x + abs_y = self.center_y + rel_y + layer_positions.append((abs_x, abs_y, angle)) + absolute_positions.append(layer_positions) + return absolute_positions + def auto_place_block(self): - if self.current_layer == 0: - # 对于第一层,直接在中心放置 - return Block(WIDTH // 2, HEIGHT // 2, 100, 20, 0, self.current_layer) + if self.current_layer >= len(self.ideal_positions): + print("塔已经完成") + return None, 0 - base_centroid = self.layer_centroids[0] if self.layer_centroids else (WIDTH // 2, HEIGHT // 2) - current_centroid = self.calculate_current_layer_centroid() or base_centroid + possible_positions = self.generate_possible_positions() + best_position, similarity_score = self.find_best_position(possible_positions) - # 计算当前层的总质量(假设质量与宽度成正比) - total_mass = sum(block.width for block in self.blocks if block.layer == self.current_layer) + if best_position: + x, y, angle = best_position + print(f"选择的最佳位置: ({x}, {y}) 角度为 {angle}") + return Block(x, y, 100, 20, angle, self.current_layer), similarity_score + else: + print("无法找到合适的放置位置") + return None, 0 + + def generate_possible_positions(self): + ideal_positions = self.ideal_positions[self.current_layer] + possible_positions = [] + + for ideal_x, ideal_y, ideal_angle in ideal_positions: + for dx in range(-self.position_tolerance, self.position_tolerance + 1, 2): + for dy in range(-self.position_tolerance, self.position_tolerance + 1, 2): + for dangle in range(-self.angle_tolerance, self.angle_tolerance + 1, 5): + x = ideal_x + dx + y = ideal_y + dy + angle = (ideal_angle + dangle) % 360 + possible_positions.append((x, y, angle)) + + print(f"生成的可能位置数量: {len(possible_positions)}") + return possible_positions + + def find_best_position(self, possible_positions): + best_position = None + min_distance = float('inf') + best_similarity_score = 0 - # 假设新积木块的质量与宽度成正比 - new_block_mass = 100 # 新积木块的宽度 + blocks_in_current_layer = sum(1 for block in self.blocks if block.layer == self.current_layer) + ideal_positions = self.ideal_positions[self.current_layer] + + if blocks_in_current_layer < len(ideal_positions): + ideal_x, ideal_y, ideal_angle = ideal_positions[blocks_in_current_layer] + else: + print("警告:当前层的所有位置都已被填满") + return None, 0 - # 计算理想位置 - ideal_x = (base_centroid[0] * (total_mass + new_block_mass) - current_centroid[0] * total_mass) / new_block_mass - ideal_y = (base_centroid[1] * (total_mass + new_block_mass) - current_centroid[1] * total_mass) / new_block_mass - - # 计算木块中心到塔重心的角度 - dx = base_centroid[0] - ideal_x - dy = base_centroid[1] - ideal_y - center_to_centroid_angle = math.degrees(math.atan2(dy, dx)) - - # 计算垂直于中心线的理想角度 - ideal_angle = (center_to_centroid_angle + 90) % 180 + 90 - - # 步骤 2: 寻找最佳角度 - best_block = None - min_angle_diff = float('inf') - - for angle in range(0, 180, 15): # 每15度尝试一次 - test_block = Block(ideal_x, ideal_y, 100, 20, angle, self.current_layer) + for x, y, angle in possible_positions: + new_block = Block(x, y, 100, 20, angle, self.current_layer) - if not self.check_interference(test_block): - # 检查是否有支撑 - if self.has_support(test_block): - # 计算与理想角度的差异 - angle_diff = min((angle - ideal_angle) % 180, (ideal_angle - angle) % 180) - if angle_diff < min_angle_diff: - min_angle_diff = angle_diff - best_block = test_block + if self.has_support(new_block) and not self.check_interference(new_block): + temp_tower = Tower(self.blueprint) + temp_tower.blocks = self.blocks.copy() + temp_tower.add_block(new_block) + if temp_tower.check_stability(): + distance = np.sqrt((x - ideal_x)**2 + (y - ideal_y)**2) + abs(angle - ideal_angle) + # 方案1:使用指数函数 + ratio = distance / (np.sqrt(WIDTH**2 + HEIGHT**2) + 360) + similarity_score = 100 * (1 - np.power(ratio, 0.5)) # 使用0.5次方增加敏感度 + + if distance < min_distance: + min_distance = distance + best_position = (x, y, angle) + best_similarity_score = similarity_score - return best_block + print(f"选择的最佳位置: {best_position},相似性评分: {best_similarity_score:.2f}") + return best_position, best_similarity_score def has_support(self, block): if self.current_layer == 0: @@ -353,10 +410,193 @@ class Tower: self.add_block_from_robot(block[0][0], block[0][1], block[1]) + def draw_tower_image(self, screen): + if self.tower_image: + image_rect = self.tower_image.get_rect() + image_rect.topright = (SCREEN_WIDTH - 10, 10) # 放置在右上角,留出10像素的边距 + screen.blit(self.tower_image, image_rect) + +def show_tower_selection(screen): + # 加载塔型图片 + tower_images = [ + pygame.image.load("tower1.png"), + pygame.image.load("tower2.png"), + pygame.image.load("tower3.png") + ] + + # 调整图片大小 + tower_images = [pygame.transform.scale(img, (BUTTON_WIDTH, BUTTON_HEIGHT)) for img in tower_images] + + # 计算按钮位置 + button_y = (SCREEN_HEIGHT - BUTTON_HEIGHT) // 2 + button_x_start = (SCREEN_WIDTH - (BUTTON_WIDTH * 3 + 40)) // 2 + + # 创建按钮矩形 + buttons = [ + pygame.Rect(button_x_start + i * (BUTTON_WIDTH + 20), button_y, BUTTON_WIDTH, BUTTON_HEIGHT) + for i in range(3) + ] + + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() + if event.type == pygame.MOUSEBUTTONDOWN: + mouse_pos = pygame.mouse.get_pos() + for i, button in enumerate(buttons): + if button.collidepoint(mouse_pos): + return i # 返回选择的塔型索引 + + screen.fill(WHITE) + + # 绘制按钮和图片 + for i, (button, image) in enumerate(zip(buttons, tower_images)): + screen.blit(image, button.topleft) + pygame.draw.rect(screen, BLACK, button, 2) + + # 添加标题 + font = pygame.font.Font(None, 36) + title = font.render("Select Tower Type", True, BLACK) + title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, 50)) + screen.blit(title, title_rect) + + pygame.display.flip() + +def polar_to_cartesian(r, theta, angle): + """ + 将极坐标转换为直角坐标 + + 参数: + r: 半径 + theta: 极角(度) + angle: 方块的旋转角度(度) + + 返回: + (x, y, angle): 直角坐标和方块旋转角度 + """ + # 将角度转换为弧度 + theta_rad = math.radians(theta) + + # 计算 x 和 y + x = r * math.cos(theta_rad) + y = r * math.sin(theta_rad) + + # 四舍五入到整数 + x = round(x) + y = round(y) + + return (x, y, angle) + +def get_tower_blueprint_1(): + # 第一种塔型的蓝图(相对坐标) + return [ + [(-40, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-35, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-30, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-25, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-20, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-15, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-10, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-5, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(0, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-5, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-10, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-15, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-20, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-25, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-30, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-35, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + [(-40, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + ] + +def get_tower_blueprint_2(): + # 使用极坐标定义第二种塔型的蓝图 + return [ + [(-30, 0, 90), (30, 0, 90)], + [(-3, 30, -5), (3, -30, -5)], + [(-29, -5, 80), (29, 5, 80)], + [(-8, 29, -15), (8, -29, -15)], + [(-28, -10, 70), (28, 10, 70)], + [(-13, 27, -25), (13, -27, -25)], + [(-27, -13, 60), (27, 13, 60)], + [(-18, 25, -35), (18, -25, -35)], + [(-26, -18, 50), (26, 18, 50)], + [(-21, 23, -45), (21, -23, -45)], + [(-25, -21, 40), (25, 21, 40)], + [(-22, 20, -55), (22, -20, -55)], + [(-24, -22, 30), (24, 22, 30)], + [(-20, 19, -65), (20, -19, -65)], + [(-23, -20, 20), (23, 20, 20)], + [(-19, 18, -75), (19, -18, -75)], + [(-22, -19, 10), (22, 19, 10)], + [(-17, 17, -85), (17, -17, -85)], + [(-21, -17, 0), (21, 17, 0)], + [(-16, 16, -95), (16, -16, -95)], + ] + +def get_tower_blueprint_3(): + # 第三种塔型的蓝图(相对坐标) + return [ + [(-60, -60, 45), (60, -60, 135), (-60, 60, 135), (60, 60, 45)], + [(0, -80, 0), (80, 0, 90), (0, 80, 0), (-80, 0, 90)], + [(-55, -55, 45), (55, -55, 135), (-55, 55, 135), (55, 55, 45)], + [(0, -75, 0), (75, 0, 90), (0, 75, 0), (-75, 0, 90)], + [(-50, -50, 45), (50, -50, 135), (-50, 50, 135), (50, 50, 45)], + [(0, -70, 0), (70, 0, 90), (0, 70, 0), (-70, 0, 90)], + [(-45, -45, 45), (45, -45, 135), (-45, 45, 135), (45, 45, 45)], + [(0, -65, 0), (65, 0, 90), (0, 65, 0), (-65, 0, 90)], + [(-40, -40, 45), (40, -40, 135), (-40, 40, 135), (40, 40, 45)], + [(0, -60, 0), (60, 0, 90), (0, 60, 0), (-60, 0, 90)], + [(-40, 0, 90), (40, 0, 90)], + [(0, 40, 0), (0, -40, 0)], + ] + def main(): - tower = Tower() + pygame.init() + screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) + pygame.display.set_caption("Stack Simulation") + + # 加载塔型图片 + tower_images = [ + pygame.image.load("tower1.png"), + pygame.image.load("tower2.png"), + pygame.image.load("tower3.png") + ] + + # 显示塔选择界面 + selected_tower = show_tower_selection(screen) + + # 根据选择的塔型设置相应的蓝图和图片 + if selected_tower == 0: + tower_blueprint = get_tower_blueprint_1() + elif selected_tower == 1: + tower_blueprint = get_tower_blueprint_2() + else: + tower_blueprint = get_tower_blueprint_3() + + tower = Tower(tower_blueprint, tower_images[selected_tower]) clock = pygame.time.Clock() current_block = None + similarity_score = 0 # 初始化相似性评分 + font = pygame.font.Font(None, 36) # 创建字体对象 running = True while running: @@ -370,9 +610,27 @@ def main(): elif event.type == pygame.MOUSEMOTION: if current_block: current_block.x, current_block.y = pygame.mouse.get_pos() + # 实时计算当前位置的相似性评分 + ideal_positions = tower.ideal_positions[tower.current_layer] + blocks_in_current_layer = sum(1 for block in tower.blocks if block.layer == tower.current_layer) + if blocks_in_current_layer < len(ideal_positions): + ideal_x, ideal_y, ideal_angle = ideal_positions[blocks_in_current_layer] + distance = np.sqrt((current_block.x - ideal_x)**2 + (current_block.y - ideal_y)**2) + abs(current_block.angle - ideal_angle) + # similarity_score = 100 * (1 - distance / (np.sqrt(WIDTH**2 + HEIGHT**2) + 360)) + ratio = distance / (np.sqrt(WIDTH**2 + HEIGHT**2) + 360) + similarity_score = 100 * (1 - np.power(ratio, 0.5)) # 使用0.5次方增加敏感度 elif event.type == pygame.MOUSEBUTTONUP: if event.button == 1 and current_block: - if tower.add_block(current_block): + # 计算最终放置位置的相似性评分 + ideal_positions = tower.ideal_positions[tower.current_layer] + blocks_in_current_layer = sum(1 for block in tower.blocks if block.layer == tower.current_layer) + if blocks_in_current_layer < len(ideal_positions): + ideal_x, ideal_y, ideal_angle = ideal_positions[blocks_in_current_layer] + distance = np.sqrt((current_block.x - ideal_x)**2 + (current_block.y - ideal_y)**2) + abs(current_block.angle - ideal_angle) + similarity_score = 100 * (1 - distance / (np.sqrt(WIDTH**2 + HEIGHT**2) + 360)) + + if tower.add_block_from_sim(current_block): + print(f"Block placed with similarity score: {similarity_score:.2f}") current_block = None else: print("CANNOT place block here, there is already a block there.") @@ -383,11 +641,15 @@ def main(): elif event.key == pygame.K_SPACE: # 按 'Space' 键确认当前层完成放置 tower.increase_layer() elif event.key == pygame.K_a: # 按 'A' 键自动放置 - auto_block = tower.auto_place_block() + auto_block, new_similarity_score = tower.auto_place_block() if auto_block: - tower.add_block(auto_block) + if tower.add_block_from_sim(auto_block): + print(f"Successfully placed block at ({auto_block.x}, {auto_block.y}) with angle {auto_block.angle}") + similarity_score = new_similarity_score + else: + print("Cannot place block") else: - print("Cannot find a suitable position for auto-placement.") + print("Cannot find suitable position") elif event.key == pygame.K_n: # 按 'N' 键自动放置新的一层 tower.auto_place_layer() elif event.key == pygame.K_s: # 按 'S' 键扫描当前层 @@ -399,6 +661,10 @@ def main(): if current_block: current_block.draw(screen) + # 显示相似性评分 + score_text = font.render(f"Similarity Score: {similarity_score:.2f}", True, (0, 0, 0)) + screen.blit(score_text, (10, 50)) + # 计算并绘制当前层的重心 current_centroid = tower.calculate_current_layer_centroid() if current_centroid: @@ -418,13 +684,15 @@ def main(): screen.blit(text, (WIDTH // 2 - text.get_width() // 2, 20)) font = pygame.font.Font(None, 36) - layer_text = font.render(f"Current layer: {tower.current_layer + 1}", True, BLACK) + layer_text = font.render(f"Current Layer: {tower.current_layer + 1}", True, BLACK) screen.blit(layer_text, (10, 10)) + tower.draw_tower_image(screen) + pygame.display.flip() clock.tick(60) pygame.quit() if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/tower1.png b/tower1.png new file mode 100644 index 0000000..5ea376c Binary files /dev/null and b/tower1.png differ diff --git a/tower2.png b/tower2.png new file mode 100644 index 0000000..288d1a4 Binary files /dev/null and b/tower2.png differ diff --git a/tower3.png b/tower3.png new file mode 100644 index 0000000..65881d6 Binary files /dev/null and b/tower3.png differ