Compare commits
No commits in common. "0f784cecc799da2e26937443c74ec01be71cc2b3" and "25e83f05dbf4a30768fd41ce636184420f946e42" have entirely different histories.
0f784cecc7
...
25e83f05db
|
@ -1,176 +0,0 @@
|
||||||
# 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,16 +3,15 @@ import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import random
|
import random
|
||||||
from utils.cv_marker import cap_and_mark
|
from utils.cv_marker import cap_and_mark
|
||||||
# from utils.stack_exe import Stackbot
|
from utils.stack_exe import Stackbot
|
||||||
import cv2
|
import cv2
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
pygame.init()
|
pygame.init()
|
||||||
|
|
||||||
WIDTH, HEIGHT = 800, 600
|
WIDTH, HEIGHT = 800, 600
|
||||||
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||||||
pygame.display.set_caption("Stack Simulation")
|
pygame.display.set_caption("积木塔仿真")
|
||||||
|
|
||||||
WHITE = (255, 255, 255)
|
WHITE = (255, 255, 255)
|
||||||
BLACK = (0, 0, 0)
|
BLACK = (0, 0, 0)
|
||||||
|
@ -29,14 +28,6 @@ cap.set(6,cv2.VideoWriter.fourcc('M','J','P','G'))
|
||||||
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
|
||||||
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
|
||||||
|
|
||||||
# 定义屏幕尺寸
|
|
||||||
SCREEN_WIDTH = 800
|
|
||||||
SCREEN_HEIGHT = 600
|
|
||||||
|
|
||||||
# 定义按钮尺寸
|
|
||||||
BUTTON_WIDTH = 200
|
|
||||||
BUTTON_HEIGHT = 200
|
|
||||||
|
|
||||||
class Block:
|
class Block:
|
||||||
def __init__(self, x, y, width, height, angle, layer):
|
def __init__(self, x, y, width, height, angle, layer):
|
||||||
self.x = x
|
self.x = x
|
||||||
|
@ -71,34 +62,27 @@ class Block:
|
||||||
|
|
||||||
|
|
||||||
class Tower:
|
class Tower:
|
||||||
def __init__(self, blueprint, tower_image=None):
|
def __init__(self):
|
||||||
self.blocks = []
|
self.blocks = []
|
||||||
self.current_layer = 0
|
self.current_layer = 0
|
||||||
self.layer_centroids = []
|
self.layer_centroids = [] # 存储每层的重心
|
||||||
self.stability_threshold = 10
|
self.stability_threshold = 10 # 稳定性阈值(像素)
|
||||||
self.rotation_angle = 15
|
self.rotation_angle = 15 # 预设每层旋转角度
|
||||||
self.angle_tolerance = 5
|
self.angle_tolerance = 5 # 允许的角度偏移
|
||||||
self.position_tolerance = 10
|
self.position_tolerance = 10 # 允许的位置偏移
|
||||||
|
|
||||||
self.sim_to_robot_matrix = np.array([
|
self.sim_to_robot_matrix = np.array([
|
||||||
[1, 0, 0, -300],
|
[1, 0, 0, -300], # 假设的转换矩阵,需要根据实际情况调整
|
||||||
[0, -1, 0, -200],
|
[0, -1, 0, -200],
|
||||||
[0, 0, 1, 0],
|
[0, 0, 1, 0],
|
||||||
[0, 0, 0, 1]
|
[0, 0, 0, 1]
|
||||||
])
|
])
|
||||||
self.robot_to_sim_matrix = np.linalg.inv(self.sim_to_robot_matrix)
|
self.robot_to_sim_matrix = np.linalg.inv(self.sim_to_robot_matrix)
|
||||||
|
|
||||||
|
self.stack_bot = Stackbot()
|
||||||
|
|
||||||
self.is_upper = False
|
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):
|
def sim_to_robot_coords(self, x, y, angle):
|
||||||
sim_coords = np.array([x, y, 0, 1])
|
sim_coords = np.array([x, y, 0, 1])
|
||||||
|
@ -122,7 +106,7 @@ class Tower:
|
||||||
angle (float): 机器人的角度
|
angle (float): 机器人的角度
|
||||||
|
|
||||||
返回值:
|
返回值:
|
||||||
bool:如果方块添加成,返回 True,否则返回 False
|
bool:如果方块添加成功,返回 True,否则返回 False
|
||||||
|
|
||||||
注意:这个函数需要先调用 robot_to_sim_coords 来将机器人坐标转换为模拟坐标,并且需要一个名为 Block 的类来创建方块对象
|
注意:这个函数需要先调用 robot_to_sim_coords 来将机器人坐标转换为模拟坐标,并且需要一个名为 Block 的类来创建方块对象
|
||||||
"""
|
"""
|
||||||
|
@ -137,27 +121,21 @@ class Tower:
|
||||||
|
|
||||||
|
|
||||||
def add_block(self, block):
|
def add_block(self, block):
|
||||||
self.add_block_from_sim(block)
|
if self.current_layer == 5:
|
||||||
return True
|
self.is_upper = True
|
||||||
# if self.current_layer == 5:
|
print("is upper")
|
||||||
# self.is_upper = True
|
|
||||||
# print("is upper")
|
|
||||||
|
|
||||||
# if not self.check_interference(block):
|
if not self.check_interference(block):
|
||||||
# self.blocks.append(block)
|
self.blocks.append(block)
|
||||||
# self.stack_bot.pick_stack()
|
self.stack_bot.pick_stack()
|
||||||
# robo_xyz = self.sim_to_robot_coords(block.x, block.y, block.angle)
|
robo_xyz = self.sim_to_robot_coords(block.x, block.y, block.angle)
|
||||||
# print(block.x, block.y, block.angle)
|
print(block.x, block.y, block.angle)
|
||||||
# if self.is_upper:
|
if self.is_upper:
|
||||||
# self.stack_bot.place_stack(robo_xyz[0], robo_xyz[1], robo_xyz[2], self.current_layer - 5)
|
self.stack_bot.place_stack(robo_xyz[0], robo_xyz[1], robo_xyz[2], self.current_layer - 5)
|
||||||
# else:
|
else:
|
||||||
# self.stack_bot.place_stack(robo_xyz[0], robo_xyz[1], robo_xyz[2], self.current_layer)
|
self.stack_bot.place_stack(robo_xyz[0], robo_xyz[1], robo_xyz[2], self.current_layer)
|
||||||
# return True
|
return True
|
||||||
# return False
|
return False
|
||||||
|
|
||||||
def add_block_from_sim(self, block):
|
|
||||||
self.blocks.append(block)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def draw(self, surface):
|
def draw(self, surface):
|
||||||
for block in self.blocks:
|
for block in self.blocks:
|
||||||
|
@ -274,84 +252,49 @@ class Tower:
|
||||||
self.layer_centroids.append(centroid)
|
self.layer_centroids.append(centroid)
|
||||||
self.current_layer += 1
|
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):
|
def auto_place_block(self):
|
||||||
if self.current_layer >= len(self.ideal_positions):
|
if self.current_layer == 0:
|
||||||
print("塔已经完成")
|
# 对于第一层,直接在中心放置
|
||||||
return None, 0
|
return Block(WIDTH // 2, HEIGHT // 2, 100, 20, 0, self.current_layer)
|
||||||
|
|
||||||
possible_positions = self.generate_possible_positions()
|
base_centroid = self.layer_centroids[0] if self.layer_centroids else (WIDTH // 2, HEIGHT // 2)
|
||||||
best_position, similarity_score = self.find_best_position(possible_positions)
|
current_centroid = self.calculate_current_layer_centroid() or base_centroid
|
||||||
|
|
||||||
if best_position:
|
# 计算当前层的总质量(假设质量与宽度成正比)
|
||||||
x, y, angle = best_position
|
total_mass = sum(block.width for block in self.blocks if block.layer == self.current_layer)
|
||||||
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
|
|
||||||
|
|
||||||
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]
|
new_block_mass = 100 # 新积木块的宽度
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
for x, y, angle in possible_positions:
|
# 计算理想位置
|
||||||
new_block = Block(x, y, 100, 20, angle, self.current_layer)
|
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)
|
||||||
|
|
||||||
if self.has_support(new_block) and not self.check_interference(new_block):
|
if not self.check_interference(test_block):
|
||||||
temp_tower = Tower(self.blueprint)
|
# 检查是否有支撑
|
||||||
temp_tower.blocks = self.blocks.copy()
|
if self.has_support(test_block):
|
||||||
temp_tower.add_block(new_block)
|
# 计算与理想角度的差异
|
||||||
if temp_tower.check_stability():
|
angle_diff = min((angle - ideal_angle) % 180, (ideal_angle - angle) % 180)
|
||||||
distance = np.sqrt((x - ideal_x)**2 + (y - ideal_y)**2) + abs(angle - ideal_angle)
|
if angle_diff < min_angle_diff:
|
||||||
# 方案1:使用指数函数
|
min_angle_diff = angle_diff
|
||||||
ratio = distance / (np.sqrt(WIDTH**2 + HEIGHT**2) + 360)
|
best_block = test_block
|
||||||
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
|
|
||||||
|
|
||||||
print(f"选择的最佳位置: {best_position},相似性评分: {best_similarity_score:.2f}")
|
return best_block
|
||||||
return best_position, best_similarity_score
|
|
||||||
|
|
||||||
def has_support(self, block):
|
def has_support(self, block):
|
||||||
if self.current_layer == 0:
|
if self.current_layer == 0:
|
||||||
|
@ -410,193 +353,10 @@ class Tower:
|
||||||
self.add_block_from_robot(block[0][0], block[0][1], block[1])
|
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():
|
def main():
|
||||||
pygame.init()
|
tower = Tower()
|
||||||
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()
|
clock = pygame.time.Clock()
|
||||||
current_block = None
|
current_block = None
|
||||||
similarity_score = 0 # 初始化相似性评分
|
|
||||||
font = pygame.font.Font(None, 36) # 创建字体对象
|
|
||||||
|
|
||||||
running = True
|
running = True
|
||||||
while running:
|
while running:
|
||||||
|
@ -610,27 +370,9 @@ def main():
|
||||||
elif event.type == pygame.MOUSEMOTION:
|
elif event.type == pygame.MOUSEMOTION:
|
||||||
if current_block:
|
if current_block:
|
||||||
current_block.x, current_block.y = pygame.mouse.get_pos()
|
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:
|
elif event.type == pygame.MOUSEBUTTONUP:
|
||||||
if event.button == 1 and current_block:
|
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
|
current_block = None
|
||||||
else:
|
else:
|
||||||
print("CANNOT place block here, there is already a block there.")
|
print("CANNOT place block here, there is already a block there.")
|
||||||
|
@ -641,15 +383,11 @@ def main():
|
||||||
elif event.key == pygame.K_SPACE: # 按 'Space' 键确认当前层完成放置
|
elif event.key == pygame.K_SPACE: # 按 'Space' 键确认当前层完成放置
|
||||||
tower.increase_layer()
|
tower.increase_layer()
|
||||||
elif event.key == pygame.K_a: # 按 'A' 键自动放置
|
elif event.key == pygame.K_a: # 按 'A' 键自动放置
|
||||||
auto_block, new_similarity_score = tower.auto_place_block()
|
auto_block = tower.auto_place_block()
|
||||||
if auto_block:
|
if auto_block:
|
||||||
if tower.add_block_from_sim(auto_block):
|
tower.add_block(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:
|
else:
|
||||||
print("Cannot find suitable position")
|
print("Cannot find a suitable position for auto-placement.")
|
||||||
elif event.key == pygame.K_n: # 按 'N' 键自动放置新的一层
|
elif event.key == pygame.K_n: # 按 'N' 键自动放置新的一层
|
||||||
tower.auto_place_layer()
|
tower.auto_place_layer()
|
||||||
elif event.key == pygame.K_s: # 按 'S' 键扫描当前层
|
elif event.key == pygame.K_s: # 按 'S' 键扫描当前层
|
||||||
|
@ -661,10 +399,6 @@ def main():
|
||||||
if current_block:
|
if current_block:
|
||||||
current_block.draw(screen)
|
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()
|
current_centroid = tower.calculate_current_layer_centroid()
|
||||||
if current_centroid:
|
if current_centroid:
|
||||||
|
@ -684,15 +418,13 @@ def main():
|
||||||
screen.blit(text, (WIDTH // 2 - text.get_width() // 2, 20))
|
screen.blit(text, (WIDTH // 2 - text.get_width() // 2, 20))
|
||||||
|
|
||||||
font = pygame.font.Font(None, 36)
|
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))
|
screen.blit(layer_text, (10, 10))
|
||||||
|
|
||||||
tower.draw_tower_image(screen)
|
|
||||||
|
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
clock.tick(60)
|
clock.tick(60)
|
||||||
|
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
BIN
tower1.png
BIN
tower1.png
Binary file not shown.
Before Width: | Height: | Size: 54 KiB |
BIN
tower2.png
BIN
tower2.png
Binary file not shown.
Before Width: | Height: | Size: 42 KiB |
BIN
tower3.png
BIN
tower3.png
Binary file not shown.
Before Width: | Height: | Size: 141 KiB |
Loading…
Reference in New Issue