https://www.bilibili.com/video/av41192590
原理,回溯法:
算法描述:
#1,随机选择一个空棋盘(不被三角板占用,不是残缺位置)
#2,然后讨论这个包含整个位置的所有可能情况,成为四种三角板中的任何一个位置(共12个),这样从由改点拼成的三角板最多有12种可能,设为n,n<=12
#3,这样无论从哪里开始拼图,都可以得到所有的解
#4,把这n种可能性都压入栈(和栈中元素不一样的才压入栈),栈元素是某个状态下的棋盘。栈中元素格式为(棋盘,当前已有三角板个数)
#5,取出栈中棋盘,回到步骤1
#6,如果栈某个元素三角板个数==(rows*cols-1)/3,,则作为可行解
#7,由于规则和顺序固定,所以应该不会有重复,免去了重复的计算过程
运行效果:
import random
import numpy as np
import cv2
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'E:\python\chessboard\main.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QPixmap,QPainter, QFont, QColor, QPen,QIcon,QBrush
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QLabel,\
QComboBox,QPushButton,QSlider,QMessageBox
class Ui_MainWindow(QMainWindow):
def __init__(self):
super(Ui_MainWindow, self).__init__()
self.setGeometry(50,50,1050, 1000)
self.comboBox = QtWidgets.QComboBox(self)
self.comboBox.setGeometry(QtCore.QRect(20, 70, 111, 31))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(True)
font.setWeight(75)
self.comboBox.setFont(font)
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("2")
self.comboBox.addItem("4")
self.comboBox.addItem("5")
self.comboBox.addItem("7")
self.comboBox.addItem("8")
self.comboBox.addItem("10")
self.label=QLabel(self)
self.label.setGeometry(QtCore.QRect(20, 30, 131, 21))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(False)
font.setWeight(50)
self.label.setFont(font)
self.label.setObjectName("label")
self.comboBox_2=QComboBox(self)
self.comboBox_2.setGeometry(QtCore.QRect(310, 70, 131, 31))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(True)
font.setWeight(75)
self.comboBox_2.setFont(font)
self.comboBox_2.setObjectName("comboBox_2")
self.label_2=QLabel(self)
self.label_2.setGeometry(QtCore.QRect(310, 40, 131, 21))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(False)
font.setWeight(50)
self.label_2.setFont(font)
self.label_2.setObjectName("label_2")
self.label_3=QLabel(self)
self.label_3.setGeometry(QtCore.QRect(0, 160, 601, 511))
font = QtGui.QFont()
font.setPointSize(15)
self.label_3.setFont(font)
self.label_3.setAutoFillBackground(True)
self.label_3.setFrameShape(QtWidgets.QFrame.Box)
self.label_3.setFrameShadow(QtWidgets.QFrame.Raised)
self.label_3.setLineWidth(2)
self.label_3.setScaledContents(True)
self.label_3.setObjectName("label_3")
self.comboBox_3=QComboBox(self)
self.comboBox_3.setGeometry(QtCore.QRect(150, 70, 141, 31))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(True)
font.setWeight(75)
self.comboBox_3.setFont(font)
self.comboBox_3.setAutoFillBackground(False)
self.comboBox_3.setObjectName("comboBox_3")
self.comboBox_3.addItem("1")
self.comboBox_3.addItem("2")
self.comboBox_3.addItem("3")
self.comboBox_3.addItem("4")
self.label_4=QLabel(self)
self.label_4.setGeometry(QtCore.QRect(150, 40, 151, 21))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(False)
font.setWeight(50)
self.label_4.setFont(font)
self.label_4.setObjectName("label_4")
self.pushButton=QPushButton(self)
self.pushButton.setGeometry(QtCore.QRect(460, 70, 131, 31))
font = QtGui.QFont()
font.setPointSize(15)
self.pushButton.setFont(font)
self.pushButton.setObjectName("pushButton")
self.label_5=QLabel(self)
self.label_5.setGeometry(QtCore.QRect(810, 250, 151, 21))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(False)
font.setWeight(50)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.label_v=QLabel(self)
self.label_v.setGeometry(QtCore.QRect(960, 190, 35, 35))
self.label_v.setFont(font)
self.horizontalSlider=QSlider(self)
self.horizontalSlider.setGeometry(QtCore.QRect(810, 200, 141, 31))
font = QtGui.QFont()
font.setPointSize(16)
self.horizontalSlider.setFont(font)
self.horizontalSlider.setMinimum(10)
self.horizontalSlider.setMaximum(100)
self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal)
self.horizontalSlider.setTickPosition(QtWidgets.QSlider.TicksBothSides)
self.horizontalSlider.setObjectName("horizontalSlider")
self.label_6=QLabel(self)
self.label_6.setGeometry(QtCore.QRect(820, 170, 141, 21))
font = QtGui.QFont()
font.setPointSize(15)
font.setBold(False)
font.setWeight(50)
self.label_6.setFont(font)
self.label_6.setObjectName("label_6")
self.comboBox_4=QComboBox(self)
self.comboBox_4.setGeometry(QtCore.QRect(820, 280, 111, 31))
font = QtGui.QFont()
font.setPointSize(15)
self.comboBox_4.setFont(font)
self.comboBox_4.setObjectName("comboBox_4")
self.pushButton_2=QPushButton(self)
self.pushButton_2.setGeometry(QtCore.QRect(820, 340, 111, 41))
self.pushButton_2.setObjectName("pushButton_2")
self.label_count_tri_type = QLabel(self)
self.label_count_tri_type.setGeometry(QtCore.QRect(820, 400, 111, 91))
self.timer = QTimer(self)
self.timer.timeout.connect(self.tick)
self.comboBox_2.currentTextChanged['QString'].connect(self.resize_label)
self.horizontalSlider.valueChanged.connect(self.show_v)
self.comboBox.currentTextChanged['QString'].connect(self.yeild_n_combox_items)
self.comboBox_3.currentTextChanged['QString'].connect(self.yeild_n_combox_items)
self.pushButton_2.clicked.connect(self.timer_control)
self.pushButton.clicked.connect(self.resolve_problem)
self.results = []
_translate = QtCore.QCoreApplication.translate
self.comboBox.setItemText(0, _translate("MainWindow", "2"))
self.comboBox.setItemText(1, _translate("MainWindow", "4"))
self.comboBox.setItemText(2, _translate("MainWindow", "5"))
self.comboBox.setItemText(3, _translate("MainWindow", "7"))
self.comboBox.setItemText(4, _translate("MainWindow", "8"))
self.comboBox.setItemText(5, _translate("MainWindow", "10"))
self.label.setText(_translate("MainWindow", "选择棋盘行数"))
self.label_2.setText(_translate("MainWindow", "选择棋盘列数"))
self.label_3.setText(_translate("MainWindow", "TextLabel"))
self.comboBox_3.setItemText(0, _translate("MainWindow", "1"))
self.comboBox_3.setItemText(1, _translate("MainWindow", "2"))
self.comboBox_3.setItemText(2, _translate("MainWindow", "3"))
self.comboBox_3.setItemText(3, _translate("MainWindow", "4"))
self.label_4.setText(_translate("MainWindow", "选择残缺位置数"))
self.pushButton.setText(_translate("MainWindow", "覆盖棋盘求解"))
self.label_5.setText(_translate("MainWindow", "演示第几种方案"))
self.label_6.setText(_translate("MainWindow", "选择显示速度"))
self.pushButton_2.setText(_translate("MainWindow", "开始演示"))
self.label_v.setText(_translate("MainWindow", str(self.horizontalSlider.value())))
self.yeild_n_combox_items()
self.show()
def draw_img(self,pix,_board,idx):
my_painter=QPainter()
my_painter.begin(pix)
color_list=[QColor.fromRgb(255,255,255),QColor.fromRgb(255,228,181),QColor.fromRgb( 65,105,225),QColor.fromRgb(124,205,124),QColor.fromRgb(125,38,205)]
for _r in range(_board.rows):
for _c in range(_board.cols):
cube_type = _board.history[idx][_r][_c]
pen=QPen(QColor.fromRgb(255,255,255),3)
my_painter.setPen(pen)
my_painter.setBrush(QColor.fromRgb(255,255,255))
if cube_type == -1:
my_painter.fillRect(_c*80,_r*80,80,80,QColor(0,0,0))
my_painter.drawLine(_c*80,_r*80,_c*80+80,_r*80+80)
my_painter.drawLine(_c*80+80,_r*80,_c*80,_r*80+80)
continue
color=color_list[cube_type]
my_painter.fillRect(_c*80,_r*80,80,80,color)
my_painter.end()
return pix
def tick(self):
pix=QPixmap(self.board_to_show.cols*80,self.board_to_show.rows*80)
pix = self.draw_img(pix,self.board_to_show, self.ticks)
self.label_3.setPixmap(pix)
self.ticks+=1
if self.ticks==len(self.board_to_show.history):
self.timer.stop()
temp_strs=["三角板" + str(idx+1)+ ":" +str(count)+"个\n" for idx,count in enumerate(self.board_to_show.count_tri_type)]
strings=""
for s in temp_strs:
strings +=s
self.label_count_tri_type.setText(strings)
def timer_control(self):
if self.comboBox_4.currentText()=='':
msg_box = QMessageBox(self)
msg_box.information(self, "警告", "没有要显示的方案", QMessageBox.Yes)
return
self.timer.start(10000/self.horizontalSlider.value())
self.board_to_show=self.results[int(self.comboBox_4.currentIndex())]
self.ticks=0
def show_v(self):
self.label_v.setText(str(self.horizontalSlider.value()))
self.timer.setInterval(10000/self.horizontalSlider.value())
def yeild_n_combox_items(self):
self.comboBox_2.clear()
_rows=int(self.comboBox.currentText())
broken_count=int(self.comboBox_3.currentText())
for i in range(10):
result=_rows*i-broken_count
if result>4 and result %3==0 and i>1 :
self.comboBox_2.addItem(str(i))
self.resize_label()
def resize_label(self):
if self.comboBox_2.currentText()==''or self.comboBox.currentText()=='':
return
self.label_3.setGeometry(0,160,int(self.comboBox_2.currentText())*80,int(self.comboBox.currentText())*80)
self.label_3.clear()
def resolve_problem(self):
if self.comboBox_2.currentText()=='':
msg_box=QMessageBox(self)
msg_box.information(self, "警告", "没有选择棋盘列数", QMessageBox.Yes)
return
rows = int(self.comboBox.currentText())
cols = int(self.comboBox_2.currentText())
self.label_3.setGeometry(0,160,80*cols,80*rows)
board = Board(_arr=np.zeros((rows, cols), np.int), _cur_tris_count=0, _rows=rows, _cols=cols)
broken_count=int(self.comboBox_3.currentText())
for i in range(broken_count):
broken_position=get_random_feasible_position(board)
board.arr[broken_position[0], broken_position[1]] = -1
total_triangular = (rows * cols - broken_count) / 3
stack_of_state = [board]
self.results = []
self.n =0
while len(stack_of_state) != 0:
self.n+=1
print(self.n)
my_board = stack_of_state.pop()
if my_board.cur_tris_count == total_triangular:
self.results.append(my_board)
continue
row, col = get_first_feasible_position(my_board)
temp_triss = []
for k in range(4):
shapes = []
tris = [(row + _r, col + _c) for _r in [0, 1] for _c in [0, 1]]
tris.pop(k)
shapes.append(tris)
tris = [(row + _r, col + _c) for _r in [0, 1] for _c in [-1, 0]]
tris.pop(k)
shapes.append(tris)
tris = [(row + _r, col + _c) for _r in [-1, 0] for _c in [0, 1]]
tris.pop(k)
shapes.append(tris)
tris = [(row + _r, col + _c) for _r in [-1, 0] for _c in [-1, 0]]
tris.pop(k)
shapes.append(tris)
shapes.pop(k)
temp_triss.append(shapes)
for idx, tris in enumerate(temp_triss):
for tri in tris:
if reasonable(my_board, tri):
temp_board = Board(my_board)
for r, c in tri:
temp_board.arr[r, c] = idx + 1
temp_board.history.append(temp_board.arr.copy())
temp_board.cur_tris_count += 1
temp_board.count_tri_type[idx]+=1
if temp_board is None:
print("a")
stack_of_state.append(temp_board)
self.comboBox_4.clear()
for i in range(len(self.results)):
self.comboBox_4.addItem(str(i))
class Board:
def __init__(self,_board=None,_arr=None,_cur_tris_count=None,_rows=None,_cols=None):
if _board is not None:
self.arr=_board.arr.copy()
self.cur_tris_count=_board.cur_tris_count
self.history=_board.history.copy()
self.cols=_board.cols
self.rows=_board.rows
self.count_tri_type=_board.count_tri_type.copy()
else:
self.arr=_arr
self.cur_tris_count=_cur_tris_count
self.history=[_arr]
self.rows=_rows
self.cols=_cols
self.count_tri_type=[0,0,0,0]
#获取一个空的位置
def get_first_feasible_position(_board):
if _board is None:
print("?")
size=np.shape(_board.arr)
for _r in range(size[0]):
for _c in range( size[1]):
if _board.arr[_r][_c]==0:
return _r,_c
return None
def get_random_feasible_position(_board):
size=np.shape(_board.arr)
temps=[]
for _r in range(size[0]):
for _c in range( size[1]):
if _board.arr[_r][_c]==0:
temps.append((_r,_c))
return temps[random.randint(0,len(temps)-1)]
#判断在棋盘中新加指定三角形之后是否合理(不重叠,不覆盖到外面)
def reasonable(_board,_tri):
for _r,_c in _tri:
if _r<0 or _r>=_board.rows:
return False
if _c<0 or _c>=_board.cols:
return False
if _board.arr[_r][_c]!=0:
return False
return True
app=QApplication(sys.argv)
ui=Ui_MainWindow()
sys.exit(app.exec_())
#1,随机选择一个空棋盘(不被三角板占用,不是残缺位置)
#2,然后讨论这个包含整个位置的所有可能情况,成为四种三角板中的任何一个位置(共12个),这样从由改点拼成的三角板最多有12种可能,设为n,n<=12
#3,这样无论从哪里开始拼图,都可以得到所有的解
#4,把这n种可能性都压入栈(和栈中元素不一样的才压入栈),栈元素是某个状态下的棋盘。栈中元素格式为(棋盘,当前已有三角板个数)
#5,取出栈中棋盘,回到步骤1
#6,如果栈某个元素三角板个数==(rows*cols-1)/3,,则作为可行解
#7,由于规则和顺序固定,所以应该不会有重复,免去了重复的计算过程