Câu trả lời:
Tôi đã nghĩ ra một giải pháp có lẽ không phải là hiệu quả nhất, nhưng nó hoạt động đủ tốt. Về cơ bản:
Điều này làm cho một ô chữ hoạt động, nhưng thường khá kém. Tôi đã thực hiện một số thay đổi đối với công thức cơ bản ở trên để đưa ra kết quả tốt hơn.
Tôi vừa mới viết của riêng mình bằng Python. Bạn có thể tìm thấy nó ở đây: http://bryanhelmig.com/python-crossword-puzzle-generator/ . Nó không tạo ra các trò chơi ô chữ kiểu NYT dày đặc, mà là kiểu ô chữ mà bạn có thể tìm thấy trong sách giải đố của một đứa trẻ.
Không giống như một số thuật toán mà tôi tìm thấy ở đó đã triển khai phương pháp đặt từ ngẫu nhiên như một số thuật toán đã đề xuất, tôi đã cố gắng triển khai phương pháp brute-force thông minh hơn một chút khi đặt từ. Đây là quy trình của tôi:
Cuối cùng, bạn đã có một câu đố ô chữ hoặc câu đố tìm kiếm từ tốt, vì chúng giống nhau. Nó có xu hướng chạy khá tốt, nhưng hãy cho tôi biết nếu bạn có bất kỳ đề xuất cải thiện nào. Lưới lớn hơn chạy chậm hơn theo cấp số nhân; danh sách từ lớn hơn một cách tuyến tính. Danh sách từ lớn hơn cũng có cơ hội cao hơn nhiều để có số vị trí từ tốt hơn.
array.sort(key=f)
ổn định, có nghĩa là (ví dụ) chỉ cần sắp xếp danh sách từ theo thứ tự bảng chữ cái theo độ dài sẽ giữ tất cả các từ 8 chữ cái được sắp xếp theo thứ tự bảng chữ cái.
Tôi thực sự đã viết một chương trình tạo ô chữ khoảng mười năm trước (nó khó hiểu nhưng các quy tắc tương tự sẽ áp dụng cho ô chữ bình thường).
Nó có một danh sách các từ (và các manh mối liên quan) được lưu trữ trong một tệp được sắp xếp theo mức độ sử dụng giảm dần cho đến ngày nay (vì vậy các từ ít được sử dụng hơn nằm ở đầu tệp). Một mẫu, về cơ bản là một mặt nạ bit đại diện cho các hình vuông màu đen và tự do, được chọn ngẫu nhiên từ một nhóm do khách hàng cung cấp.
Sau đó, đối với mỗi từ không hoàn chỉnh trong câu đố (về cơ bản tìm ô trống đầu tiên và xem từ bên phải (từ ngang) hay từ bên dưới (từ dưới) cũng trống), một tìm kiếm đã được thực hiện tệp tìm kiếm từ đầu tiên phù hợp, có tính đến các chữ cái đã có trong từ đó. Nếu không có từ nào có thể phù hợp, bạn chỉ cần đánh dấu toàn bộ từ đó là chưa hoàn thành và tiếp tục.
Ở cuối sẽ là một số từ chưa hoàn thành mà trình biên dịch sẽ phải điền vào (và thêm từ đó và một đầu mối vào tệp nếu muốn). Nếu họ không thể đưa ra bất kỳ ý tưởng nào , họ có thể chỉnh sửa ô chữ theo cách thủ công để thay đổi các ràng buộc hoặc chỉ yêu cầu tạo lại toàn bộ.
Khi tệp từ / đầu mối đạt đến kích thước nhất định (và nó đã thêm 50-100 manh mối mỗi ngày cho ứng dụng khách này), hiếm khi có trường hợp phải thực hiện nhiều hơn hai hoặc ba lần sửa lỗi thủ công cho mỗi ô chữ .
Thuật toán này tạo ra 50 ô chữ mũi tên 6x9 dày đặc trong 60 giây. Nó sử dụng cơ sở dữ liệu từ (với từ + mẹo) và cơ sở dữ liệu bảng (với các bảng được cấu hình sẵn).
1) Search for all starting cells (the ones with an arrow), store their size and directions
2) Loop through all starting cells
2.1) Search a word
2.1.1) Check if it was not already used
2.1.2) Check if it fits
2.2) Add the word to the board
3) Check if all cells were filled
Một cơ sở dữ liệu từ lớn hơn sẽ giảm đáng kể thời gian tạo và một số loại bảng khó điền hơn! Các bảng lớn hơn đòi hỏi nhiều thời gian hơn để được điền chính xác!
Thí dụ:
Bảng 6x9 được cấu hình trước:
(# có nghĩa là một mẹo trong một ô,% có nghĩa là hai mẹo trong một ô, các mũi tên không được hiển thị)
# - # # - % # - #
- - - - - - - - -
# - - - - - # - -
% - - # - # - - -
% - - - - - % - -
- - - - - - - - -
Bảng 6x9 được tạo:
# C # # P % # O #
S A T E L L I T E
# N I N E S # T A
% A B # A # G A S
% D E N S E % W E
C A T H E D R A L
Mẹo [dòng, cột]:
[1,0] SATELLITE: Used for weather forecast
[5,0] CATHEDRAL: The principal church of a city
[0,1] CANADA: Country on USA's northern border
[0,4] PLEASE: A polite way to ask things
[0,7] OTTAWA: Canada's capital
[1,2] TIBET: Dalai Lama's region
[1,8] EASEL: A tripod used to put a painting
[2,1] NINES: Dressed up to (?)
[4,1] DENSE: Thick; impenetrable
[3,6] GAS: Type of fuel
[1,5] LS: Lori Singer, american actress
[2,7] TA: Teaching assistant (abbr.)
[3,1] AB: A blood type
[4,3] NH: New Hampshire (abbr.)
[4,5] ED: (?) Harris, american actor
[4,7] WE: The first person of plural (Grammar)
Mặc dù đây là một câu hỏi cũ hơn, nhưng sẽ cố gắng trả lời dựa trên công việc tương tự mà tôi đã làm.
Có nhiều cách tiếp cận để giải quyết các vấn đề về ràng buộc (thường nằm trong lớp phức tạp NPC).
Điều này liên quan đến tối ưu hóa tổ hợp và lập trình ràng buộc. Trong trường hợp này, các ràng buộc là hình dạng của lưới và yêu cầu các từ là duy nhất, v.v.
Cách tiếp cận ngẫu nhiên hóa / ủ cũng có thể hoạt động (mặc dù trong cài đặt thích hợp).
Sự đơn giản hiệu quả có thể chỉ là sự khôn ngoan cuối cùng!
Các yêu cầu dành cho trình biên dịch ô chữ hoàn chỉnh hơn hoặc ít hơn và trình tạo (WYSIWYG trực quan).
Bỏ qua phần xây dựng WYSIWYG, phác thảo trình biên dịch là:
Tải danh sách từ có sẵn (được sắp xếp theo độ dài từ, tức là 2,3, .., 20)
Tìm các ô từ (tức là các từ lưới) trên lưới do người dùng tạo (ví dụ: từ x, y với độ dài L, ngang hoặc dọc) (độ phức tạp O (N))
Tính các điểm giao nhau của các từ lưới (cần được điền) (độ phức tạp O (N ^ 2))
Tính toán các giao điểm của các từ trong danh sách từ với các chữ cái khác nhau của bảng chữ cái được sử dụng (điều này cho phép tìm kiếm các từ phù hợp bằng cách sử dụng một mẫu ví dụ: Luận án Sik Cambon được cwc sử dụng ) (độ phức tạp O (WL * AL))
Các bước .3 và .4 cho phép thực hiện tác vụ này:
a. Các giao điểm của các từ lưới với chính nó cho phép tạo "mẫu" để cố gắng tìm các kết quả phù hợp trong danh sách từ liên quan gồm các từ có sẵn cho từ lưới này (bằng cách sử dụng các chữ cái của các từ giao nhau với từ này đã được điền vào một số bước của thuật toán)
b. Các giao điểm của các từ trong danh sách từ với bảng chữ cái cho phép tìm các từ phù hợp (ứng cử viên) phù hợp với một "mẫu" nhất định (ví dụ: 'A' ở vị trí đầu tiên và 'B' ở vị trí thứ 3, v.v.)
Vì vậy, với các cấu trúc dữ liệu được triển khai, thuật toán được sử dụng là như thế này:
LƯU Ý: nếu lưới và cơ sở dữ liệu từ không đổi, các bước trước đó chỉ có thể được thực hiện một lần.
Bước đầu tiên của thuật toán là chọn ngẫu nhiên một ô từ trống (từ lưới) và điền nó bằng một từ ứng viên từ danh sách từ liên quan của nó (ngẫu nhiên hóa cho phép tạo ra các chất giải khác nhau trong các lần thực hiện liên tiếp của thuật toán) (độ phức tạp O (1) hoặc O ( N))
Đối với mỗi ô từ còn trống (có giao điểm với ô từ đã được điền), hãy tính tỷ lệ ràng buộc (tỷ lệ này có thể khác nhau, đơn giản thứ là số lượng giải pháp có sẵn ở bước đó) và sắp xếp các ô từ trống theo tỷ lệ này (độ phức tạp O (NlogN ) hoặc O (N))
Lặp lại các ô từ trống đã tính ở bước trước và với mỗi ô, hãy thử một số giải pháp cancdidate (đảm bảo rằng "tính nhất quán cung được giữ lại", tức là lưới có một giải pháp sau bước này nếu từ này được sử dụng) và sắp xếp chúng theo tính khả dụng tối đa cho bước tiếp theo (tức là bước tiếp theo có giải pháp tối đa có thể nếu từ này được sử dụng tại thời điểm đó ở nơi đó, v.v.) (độ phức tạp O (N * MaxCandidatesUsed))
Điền từ đó (đánh dấu là đã điền và chuyển sang bước 2)
Nếu không tìm thấy từ nào đáp ứng các tiêu chí của bước .3, hãy thử quay lại một giải pháp ứng viên khác của một số bước trước đó (tiêu chí có thể thay đổi ở đây) (độ phức tạp O (N))
Nếu tìm thấy backtrack, hãy sử dụng tùy chọn thay thế và tùy chọn đặt lại bất kỳ từ nào đã được điền mà có thể cần đặt lại (đánh dấu chúng là chưa điền lại) (độ phức tạp O (N))
Nếu không tìm thấy backtrack, thì không thể tìm thấy giải pháp nào (ít nhất là với cấu hình này, hạt giống ban đầu, v.v.)
Khác khi tất cả các ô từ được lấp đầy, bạn có một giải pháp
Thuật toán này thực hiện một bước đi nhất quán ngẫu nhiên của cây giải pháp của bài toán. Nếu tại một thời điểm nào đó có một điểm cuối, nó sẽ quay trở lại một nút trước đó và đi theo một lộ trình khác. Chưa tìm thấy giải pháp nào hoặc số lượng ứng viên cho các nút khác nhau đã cạn kiệt.
Phần nhất quán đảm bảo rằng một giải pháp được tìm thấy thực sự là một giải pháp và phần ngẫu nhiên cho phép tạo ra các giải pháp khác nhau trong các lần thực thi khác nhau và ở mức trung bình cũng có hiệu suất tốt hơn.
Tái bút. tất cả điều này (và những thứ khác) được triển khai bằng JavaScript thuần túy (với khả năng xử lý song song và WYSIWYG)
PS2. Thuật toán có thể dễ dàng song song hóa để tạo ra nhiều giải pháp (khác nhau) cùng một lúc
Hi vọng điêu nay co ich
Tại sao không chỉ sử dụng phương pháp xác suất ngẫu nhiên để bắt đầu. Bắt đầu với một từ, sau đó liên tục chọn một từ ngẫu nhiên và cố gắng lắp nó vào trạng thái hiện tại của câu đố mà không phá vỡ các ràng buộc về kích thước, v.v. Nếu bạn thất bại, chỉ cần bắt đầu lại từ đầu.
Bạn sẽ ngạc nhiên về mức độ thường xuyên của một cách tiếp cận Monte Carlo như thế này.
Đây là một số mã JavaScript dựa trên câu trả lời của nickf và mã Python của Bryan. Chỉ đăng nó trong trường hợp người khác cần nó trong js.
function board(cols, rows) { //instantiator object for making gameboards
this.cols = cols;
this.rows = rows;
var activeWordList = []; //keeps array of words actually placed in board
var acrossCount = 0;
var downCount = 0;
var grid = new Array(cols); //create 2 dimensional array for letter grid
for (var i = 0; i < rows; i++) {
grid[i] = new Array(rows);
}
for (var x = 0; x < cols; x++) {
for (var y = 0; y < rows; y++) {
grid[x][y] = {};
grid[x][y].targetChar = EMPTYCHAR; //target character, hidden
grid[x][y].indexDisplay = ''; //used to display index number of word start
grid[x][y].value = '-'; //actual current letter shown on board
}
}
function suggestCoords(word) { //search for potential cross placement locations
var c = '';
coordCount = [];
coordCount = 0;
for (i = 0; i < word.length; i++) { //cycle through each character of the word
for (x = 0; x < GRID_HEIGHT; x++) {
for (y = 0; y < GRID_WIDTH; y++) {
c = word[i];
if (grid[x][y].targetChar == c) { //check for letter match in cell
if (x - i + 1> 0 && x - i + word.length-1 < GRID_HEIGHT) { //would fit vertically?
coordList[coordCount] = {};
coordList[coordCount].x = x - i;
coordList[coordCount].y = y;
coordList[coordCount].score = 0;
coordList[coordCount].vertical = true;
coordCount++;
}
if (y - i + 1 > 0 && y - i + word.length-1 < GRID_WIDTH) { //would fit horizontally?
coordList[coordCount] = {};
coordList[coordCount].x = x;
coordList[coordCount].y = y - i;
coordList[coordCount].score = 0;
coordList[coordCount].vertical = false;
coordCount++;
}
}
}
}
}
}
function checkFitScore(word, x, y, vertical) {
var fitScore = 1; //default is 1, 2+ has crosses, 0 is invalid due to collision
if (vertical) { //vertical checking
for (i = 0; i < word.length; i++) {
if (i == 0 && x > 0) { //check for empty space preceeding first character of word if not on edge
if (grid[x - 1][y].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
} else if (i == word.length && x < GRID_HEIGHT) { //check for empty space after last character of word if not on edge
if (grid[x+i+1][y].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
if (x + i < GRID_HEIGHT) {
if (grid[x + i][y].targetChar == word[i]) { //letter match - aka cross point
fitScore += 1;
} else if (grid[x + i][y].targetChar != EMPTYCHAR) { //letter doesn't match and it isn't empty so there is a collision
fitScore = 0;
break;
} else { //verify that there aren't letters on either side of placement if it isn't a crosspoint
if (y < GRID_WIDTH - 1) { //check right side if it isn't on the edge
if (grid[x + i][y + 1].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
if (y > 0) { //check left side if it isn't on the edge
if (grid[x + i][y - 1].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
}
}
}
} else { //horizontal checking
for (i = 0; i < word.length; i++) {
if (i == 0 && y > 0) { //check for empty space preceeding first character of word if not on edge
if (grid[x][y-1].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
} else if (i == word.length - 1 && y + i < GRID_WIDTH -1) { //check for empty space after last character of word if not on edge
if (grid[x][y + i + 1].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
if (y + i < GRID_WIDTH) {
if (grid[x][y + i].targetChar == word[i]) { //letter match - aka cross point
fitScore += 1;
} else if (grid[x][y + i].targetChar != EMPTYCHAR) { //letter doesn't match and it isn't empty so there is a collision
fitScore = 0;
break;
} else { //verify that there aren't letters on either side of placement if it isn't a crosspoint
if (x < GRID_HEIGHT) { //check top side if it isn't on the edge
if (grid[x + 1][y + i].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
if (x > 0) { //check bottom side if it isn't on the edge
if (grid[x - 1][y + i].targetChar != EMPTYCHAR) { //adjacent letter collision
fitScore = 0;
break;
}
}
}
}
}
}
return fitScore;
}
function placeWord(word, clue, x, y, vertical) { //places a new active word on the board
var wordPlaced = false;
if (vertical) {
if (word.length + x < GRID_HEIGHT) {
for (i = 0; i < word.length; i++) {
grid[x + i][y].targetChar = word[i];
}
wordPlaced = true;
}
} else {
if (word.length + y < GRID_WIDTH) {
for (i = 0; i < word.length; i++) {
grid[x][y + i].targetChar = word[i];
}
wordPlaced = true;
}
}
if (wordPlaced) {
var currentIndex = activeWordList.length;
activeWordList[currentIndex] = {};
activeWordList[currentIndex].word = word;
activeWordList[currentIndex].clue = clue;
activeWordList[currentIndex].x = x;
activeWordList[currentIndex].y = y;
activeWordList[currentIndex].vertical = vertical;
if (activeWordList[currentIndex].vertical) {
downCount++;
activeWordList[currentIndex].number = downCount;
} else {
acrossCount++;
activeWordList[currentIndex].number = acrossCount;
}
}
}
function isActiveWord(word) {
if (activeWordList.length > 0) {
for (var w = 0; w < activeWordList.length; w++) {
if (word == activeWordList[w].word) {
//console.log(word + ' in activeWordList');
return true;
}
}
}
return false;
}
this.displayGrid = function displayGrid() {
var rowStr = "";
for (var x = 0; x < cols; x++) {
for (var y = 0; y < rows; y++) {
rowStr += "<td>" + grid[x][y].targetChar + "</td>";
}
$('#tempTable').append("<tr>" + rowStr + "</tr>");
rowStr = "";
}
console.log('across ' + acrossCount);
console.log('down ' + downCount);
}
//for each word in the source array we test where it can fit on the board and then test those locations for validity against other already placed words
this.generateBoard = function generateBoard(seed = 0) {
var bestScoreIndex = 0;
var top = 0;
var fitScore = 0;
var startTime;
//manually place the longest word horizontally at 0,0, try others if the generated board is too weak
placeWord(wordArray[seed].word, wordArray[seed].displayWord, wordArray[seed].clue, 0, 0, false);
//attempt to fill the rest of the board
for (var iy = 0; iy < FIT_ATTEMPTS; iy++) { //usually 2 times is enough for max fill potential
for (var ix = 1; ix < wordArray.length; ix++) {
if (!isActiveWord(wordArray[ix].word)) { //only add if not already in the active word list
topScore = 0;
bestScoreIndex = 0;
suggestCoords(wordArray[ix].word); //fills coordList and coordCount
coordList = shuffleArray(coordList); //adds some randomization
if (coordList[0]) {
for (c = 0; c < coordList.length; c++) { //get the best fit score from the list of possible valid coordinates
fitScore = checkFitScore(wordArray[ix].word, coordList[c].x, coordList[c].y, coordList[c].vertical);
if (fitScore > topScore) {
topScore = fitScore;
bestScoreIndex = c;
}
}
}
if (topScore > 1) { //only place a word if it has a fitscore of 2 or higher
placeWord(wordArray[ix].word, wordArray[ix].clue, coordList[bestScoreIndex].x, coordList[bestScoreIndex].y, coordList[bestScoreIndex].vertical);
}
}
}
}
if(activeWordList.length < wordArray.length/2) { //regenerate board if if less than half the words were placed
seed++;
generateBoard(seed);
}
}
}
function seedBoard() {
gameboard = new board(GRID_WIDTH, GRID_HEIGHT);
gameboard.generateBoard();
gameboard.displayGrid();
}
Tôi sẽ tạo ra hai con số: Độ dài và Điểm số Scrabble. Giả sử rằng điểm Scrabble thấp có nghĩa là bạn sẽ dễ dàng tham gia hơn (điểm thấp = nhiều chữ cái phổ biến). Sắp xếp danh sách theo độ dài giảm dần và điểm Scrabble tăng dần.
Tiếp theo, chỉ cần đi xuống danh sách. Nếu từ đó không giao nhau với một từ hiện có (kiểm tra từng từ theo độ dài của chúng và điểm Scrabble tương ứng), thì hãy đưa nó vào hàng đợi và kiểm tra từ tiếp theo.
Rửa sạch và lặp lại, và điều này sẽ tạo ra một ô chữ.
Tất nhiên, tôi khá chắc rằng đây là O (n!) Và không đảm bảo sẽ hoàn thành ô chữ cho bạn, nhưng có lẽ ai đó có thể cải thiện nó.
Tôi đã suy nghĩ về vấn đề này. Cảm nhận của tôi là để tạo ra một ô chữ thực sự dày đặc, bạn không thể hy vọng rằng danh sách từ giới hạn của bạn là đủ. Do đó, bạn có thể muốn lấy một từ điển và đặt nó vào cấu trúc dữ liệu "trie". Điều này sẽ cho phép bạn dễ dàng tìm thấy các từ điền vào khoảng trống còn lại. Trong một trie, khá hiệu quả để triển khai một truyền tải, chẳng hạn như, cung cấp cho bạn tất cả các từ có dạng "c? T".
Vì vậy, suy nghĩ chung của tôi là: tạo ra một số cách tiếp cận tương đối thô bạo như một số mô tả ở đây để tạo ra một chữ thập mật độ thấp và điền vào chỗ trống bằng các từ điển.
Nếu ai khác đã thực hiện phương pháp này, vui lòng cho tôi biết.
Tôi đã chơi xung quanh công cụ tạo ô chữ và tôi thấy điều này là quan trọng nhất:
0.!/usr/bin/python
a. allwords.sort(key=len, reverse=True)
b. tạo một số mục / đối tượng giống như con trỏ sẽ đi quanh ma trận để dễ định hướng trừ khi bạn muốn lặp lại theo lựa chọn ngẫu nhiên sau này.
đầu tiên, chọn cặp đầu tiên và đặt chúng ngang và xuống từ 0,0; lưu trữ cái đầu tiên làm 'thủ lĩnh' ô chữ hiện tại của chúng tôi.
di chuyển con trỏ theo thứ tự đường chéo hoặc ngẫu nhiên với xác suất đường chéo lớn hơn đến ô trống tiếp theo
lặp lại các từ như và sử dụng độ dài không gian trống để xác định độ dài từ tối đa:
temp=[]
for w_size in range( len( w_space ), 2, -1 ) :
# t
for w in [ word for word in allwords if len(word) == w_size ] :
#
if w not in temp and putTheWord( w, w_space ) :
#
temp.append( w )
để so sánh từ với không gian trống, tôi đã sử dụng tức là:
w_space=['c','.','a','.','.','.'] # whereas dots are blank cells
# CONVERT MULTIPLE '.' INTO '.*' FOR REGEX
pattern = r''.join( [ x.letter for x in w_space ] )
pattern = pattern.strip('.') +'.*' if pattern[-1] == '.' else pattern
prog = re.compile( pattern, re.U | re.I )
if prog.match( w ) :
#
if prog.match( w ).group() == w :
#
return True
sau mỗi từ được sử dụng thành công, hãy đổi hướng. Lặp lại trong khi tất cả các ô được điền HOẶC bạn hết từ HOẶC giới hạn số lần lặp thì:
# CHANGE ALL WORDS LIST
inexOf1stWord = allwords.index( leading_w )
allwords = allwords[:inexOf1stWord+1][:] + allwords[inexOf1stWord+1:][:]
... và lặp lại một lần nữa ô chữ mới.
Tạo hệ thống tính điểm bằng cách dễ dàng điền và một số thước đo ước tính. Cho điểm cho ô chữ hiện tại và thu hẹp lựa chọn sau này bằng cách thêm nó vào danh sách ô chữ đã thực hiện nếu hệ thống tính điểm của bạn hài lòng với điểm số.
Sau phiên lặp lại đầu tiên, hãy lặp lại một lần nữa từ danh sách các ô chữ đã tạo để hoàn thành công việc.
Bằng cách sử dụng nhiều tham số hơn, tốc độ có thể được cải thiện bởi một yếu tố rất lớn.
Tôi sẽ nhận được một chỉ mục của mỗi chữ cái được sử dụng bởi mỗi từ để biết các dấu gạch chéo có thể. Sau đó, tôi sẽ chọn từ lớn nhất và sử dụng nó làm cơ sở. Chọn cái lớn tiếp theo và vượt qua nó. Rửa sạch và lặp lại. Nó có thể là một vấn đề NP.
Một ý tưởng khác là tạo ra một thuật toán di truyền trong đó chỉ số độ mạnh là số lượng từ bạn có thể đưa vào lưới.
Phần khó tôi thấy là khi nào thì không thể bỏ qua một danh sách nào đó.
Cái này xuất hiện như một dự án trong khóa học AI CS50 của Harvard. Ý tưởng là hình thành vấn đề tạo ô chữ như một vấn đề thỏa mãn hạn chế và giải quyết nó bằng cách quay ngược với các phương pháp heuristics khác nhau để giảm không gian tìm kiếm.
Để bắt đầu, chúng ta cần một vài tệp đầu vào:
`
###_####_#
____####_#
_##_#_____
_##_#_##_#
______####
#_###_####
#_##______
#_###_##_#
_____###_#
#_######_#
##_______#
`
Một từ vựng đầu vào (danh sách từ / từ điển) từ đó các từ ứng viên sẽ được chọn (như hình sau).
a
abandon
ability
able
abortion
about
above
abroad
absence
absolute
absolutely
...
Bây giờ CSP được xác định và giải quyết như sau:
Phần sau cho thấy kết quả thu được bằng cách triển khai thuật toán giải CSP:
`
███S████D█
MUCH████E█
E██A█AGENT
S██R█N██Y█
SUPPLY████
█N███O████
█I██INSIDE
█Q███E██A█
SUGAR███N█
█E██████C█
██OFFENSE█
`
Hình ảnh động sau đây cho thấy các bước quay lại:
Đây là một từ khác có danh sách từ tiếng Bangla (tiếng Bengali):
Tôi đã viết mã giải pháp JavaScript / jQuery cho vấn đề này:
Demo mẫu: http://www.earthfluent.com/crossword-puzzle-demo.html
Mã nguồn: https://github.com/HoldOffHunger/jquery-crossword-puzzle-generator
Mục đích của thuật toán tôi đã sử dụng:
Tôi sẽ mô tả thuật toán tôi đã sử dụng:
Nhóm các từ lại với nhau theo những từ có chung một chữ cái.
Từ các nhóm này, xây dựng các tập hợp cấu trúc dữ liệu mới ("khối từ"), là một từ chính (chạy qua tất cả các từ khác) và sau đó là các từ khác (chạy qua từ chính).
Bắt đầu trò chơi ô chữ với khối chữ đầu tiên ở vị trí trên cùng bên trái của trò chơi ô chữ.
Đối với phần còn lại của các khối từ, bắt đầu từ vị trí dưới cùng bên phải của trò chơi ô chữ, di chuyển lên trên và sang trái cho đến khi không còn chỗ trống nào để điền. Nếu có nhiều cột trống hướng lên trên hơn bên trái, hãy di chuyển lên trên và ngược lại.
var crosswords = generateCrosswordBlockSources(puzzlewords);
. Chỉ cần bảng điều khiển ghi giá trị này. Đừng quên, có một "chế độ ăn gian" trong trò chơi, nơi bạn chỉ cần nhấp vào "Tiết lộ câu trả lời", để nhận giá trị ngay lập tức.