Compare commits
4 Commits
25e83f05db
...
0f784cecc7
Author | SHA1 | Date |
---|---|---|
|
0f784cecc7 | |
|
0f758396a7 | |
|
f0096d2c74 | |
|
53ccf4439c |
|
@ -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
|
406
game_plat.py
406
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()
|
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
Binary file not shown.
After Width: | Height: | Size: 141 KiB |
Loading…
Reference in New Issue