답변:
나는 아마도 가장 효율적이지는 않지만 충분히 잘 작동하는 솔루션을 생각해 냈습니다. 원래:
이것은 작동하지만 종종 매우 좋지 않은 십자말 풀이를 만듭니다. 더 나은 결과를 얻기 위해 위의 기본 레시피를 여러 가지 변경했습니다.
나는 최근에 파이썬으로 직접 작성했습니다. http://bryanhelmig.com/python-crossword-puzzle-generator/에서 찾을 수 있습니다 . 조밀 한 NYT 스타일의 십자말 풀이는 아니지만 어린이의 퍼즐 책에서 찾을 수있는 십자말 풀이 스타일입니다.
몇 가지 제안한 단어를 배치하는 무작위 무차별 대입 방법을 구현 한 몇 가지 알고리즘과 달리, 저는 단어 배치에서 약간 더 똑똑한 무차별 대입 접근 방식을 구현하려고했습니다. 내 프로세스는 다음과 같습니다.
결국, 당신은 괜찮은 십자말 풀이 또는 단어 검색 퍼즐이 거의 같습니다. 다소 잘 돌아가는 경향이 있지만 개선에 대한 제안이 있으면 알려주십시오. 더 큰 그리드는 기하 급수적으로 느리게 실행됩니다. 더 큰 단어는 선형으로 나열됩니다. 더 큰 단어 목록은 더 나은 단어 배치 번호를 얻을 가능성이 훨씬 더 높습니다.
array.sort(key=f)
은 안정적입니다. 즉, (예를 들어) 단순히 알파벳순 단어 목록을 길이별로 정렬하면 모든 8 글자 단어가 알파벳순으로 정렬됩니다.
나는 실제로 십자말 풀이 생성 프로그램을 약 10 년 전에 썼습니다 (이는 수수께끼 였지만 일반적인 십자말 풀이에도 동일한 규칙이 적용될 것입니다).
파일에 저장된 단어 목록 (및 관련 단서)이 현재까지 내림차순으로 정렬되어있어 덜 사용되는 단어가 파일 맨 위에 표시됩니다. 기본적으로 검은 색 사각형과 자유 사각형을 나타내는 비트 마스크 인 템플릿은 클라이언트가 제공 한 풀에서 무작위로 선택되었습니다.
그런 다음 퍼즐의 완성되지 않은 각 단어에 대해 (기본적으로 첫 번째 빈 사각형을 찾고 오른쪽에있는 단어 (교차 단어) 또는 아래에있는 단어 (아래 단어)도 비어 있는지 확인) 검색이 수행되었습니다. 해당 단어에 이미있는 문자를 고려하여 맞는 첫 번째 단어를 찾는 파일. 적합 할 수있는 단어가 없다면 전체 단어를 불완전한 것으로 표시하고 계속 진행했습니다.
마지막에는 컴파일러가 채워야하는 완료되지 않은 단어가있을 것입니다 (원하는 경우 파일에 단어와 단서를 추가). 그들이 가지고 올 수없는 경우 모든 아이디어, 그들은 제약 조건을 변경하거나 전체 재생성을 요청 수동으로 크로스 워드 퍼즐, 편집 할 수 있습니다.
단어 / 단서 파일이 특정 크기에 도달하면 (이 클라이언트에 대해 하루에 50-100 개의 단서를 추가 함) 각 십자말 풀이에 대해 수행해야하는 수동 수정 작업이 두세 번 이상 발생하는 경우는 거의 없었습니다. .
이 알고리즘은 60 초 내에 50 개의 조밀 한 6x9 화살표 십자말 풀이 를 만듭니다 . 단어 데이터베이스 (단어 + 팁 포함)와 보드 데이터베이스 (사전 구성된 보드 포함)를 사용합니다.
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
더 큰 단어 데이터베이스는 생성 시간을 상당히 줄여주고 어떤 종류의 보드는 채우기가 더 어렵습니다! 보드가 클수록 올바르게 채우려면 더 많은 시간이 필요합니다!
예:
사전 구성된 6x9 보드 :
(#은 하나의 셀에 하나의 팁을 의미하고 %는 하나의 셀에 두 개의 팁을 의미하며 화살표는 표시되지 않음)
# - # # - % # - #
- - - - - - - - -
# - - - - - # - -
% - - # - # - - -
% - - - - - % - -
- - - - - - - - -
6x9 보드 생성 :
# 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
팁 [줄, 열] :
[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)
이것은 오래된 질문이지만 내가 한 비슷한 작업을 기반으로 답변을 시도합니다.
제약 문제를 해결하는 방법에는 여러 가지가 있습니다 (일반 레이아웃은 NPC 복잡성 클래스에 있음).
이것은 조합 최적화 및 제약 프로그래밍과 관련이 있습니다. 이 경우 제약 조건은 그리드의 기하학적 구조와 단어가 고유해야한다는 요구 사항 등입니다.
임의 화 / 어닐링 접근 방식도 작동 할 수 있습니다 (적절한 설정 내에서).
효율적인 단순함이 궁극적 인 지혜 일 수 있습니다!
요구 사항은 다소 완전한 크로스 워드 컴파일러 및 (시각적 WYSIWYG) 빌더에 대한 것이 었습니다.
WYSIWYG 빌더 부분을 제쳐두고 컴파일러 개요는 다음과 같습니다.
사용 가능한 단어 목록로드 (단어 길이별로 정렬, 즉 2,3, .., 20)
사용자가 구성한 그리드에서 단어 슬롯 (예 : 그리드 단어)을 찾습니다 (예 : 길이가 L, 수평 또는 수직 인 x, y의 단어) (복잡도 O (N))
채워야하는 그리드 단어의 교차점 계산 (복잡도 O (N ^ 2))
사용 된 알파벳의 다양한 문자와 단어 목록의 단어의 교차점을 계산합니다 (이를 통해 템플릿을 사용하여 일치하는 단어를 검색 할 수 있습니다 (예 : cwc에서 사용하는 Sik Cambon 논문 ). 복잡도 O (WL * AL)).
.3 및 .4 단계에서이 작업을 수행 할 수 있습니다.
ㅏ. 그리드 단어와 그 자체의 교차점은이 그리드 단어에 대해 사용 가능한 단어의 관련 단어 목록에서 일치하는 단어를 찾기위한 "템플릿"을 만들 수 있습니다 (이미 특정 단어에 이미 채워진이 단어와 교차하는 다른 단어의 문자를 사용하여 알고리즘 단계)
비. 단어 목록의 단어와 알파벳의 교차점을 통해 주어진 "템플릿"과 일치하는 일치하는 (후보) 단어를 찾을 수 있습니다 (예 : 'A'는 1 위, 'B'는 3 위 등).
따라서 이러한 데이터 구조를 구현하면 사용 된 알고리즘은 다음과 같습니다.
참고 : 그리드와 단어 데이터베이스가 일정하면 이전 단계를 한 번만 수행 할 수 있습니다.
알고리즘의 첫 번째 단계는 빈 단어 슬롯 (그리드 단어)을 무작위로 선택하고 연관된 단어 목록에서 후보 단어로 채우는 것입니다 (무작위 화는 알고리즘의 연속 실행에서 다른 솔루션을 생성 할 수 있음) (복잡도 O (1) 또는 O ( N))
여전히 비어있는 각 단어 슬롯 (이미 채워진 단어 슬롯과 교차가 있음)에 대해 제약 비율을 계산하고 (이것은 다를 수 있으며, sth simple은 해당 단계에서 사용 가능한 솔루션의 수입니다)이 비율에 따라 빈 단어 슬롯을 정렬합니다 (복잡도 O (NlogN ) 또는 O (N))
이전 단계에서 계산 된 빈 단어 슬롯을 반복하고 각각에 대해 여러 후보 솔루션을 시도합니다 ( "arc-consistency가 유지됨"확인, 즉이 단어가 사용되는 경우이 단계 이후에 그리드에 솔루션이 있음). 다음 단계에 대한 최대 가용성 (즉,이 단어가 해당 장소에서 해당 시간에 사용되는 경우 다음 단계에는 가능한 최대 솔루션이 있습니다.) (복잡도 O (N * MaxCandidatesUsed))
해당 단어를 채우십시오 (채워진 것으로 표시하고 2 단계로 이동).
단계 .3의 기준을 충족하는 단어가 발견되지 않으면 이전 단계의 다른 후보 솔루션으로 역 추적 해보십시오 (기준은 여기에서 다를 수 있음) (복잡도 O (N)).
역 추적이 발견되면 대체를 사용하고 선택적으로 재설정이 필요할 수있는 이미 채워진 단어를 재설정합니다 (다시 채워지지 않은 것으로 표시) (복잡도 O (N))
역 추적을 찾지 못하면 솔루션을 찾을 수 없습니다 (적어도이 구성, 초기 시드 등).
그렇지 않으면 모든 워드 롯이 채워지면 하나의 솔루션이 있습니다.
이 알고리즘은 문제의 솔루션 트리를 무작위로 일관되게 진행합니다. 어떤 지점에서 막 다른 골목이 있으면 이전 노드로 역 추적하고 다른 경로를 따릅니다. 솔루션이 발견되거나 다양한 노드에 대한 후보 수가 소진 될 때까지.
일관성 부분은 발견 된 솔루션이 실제로 솔루션인지 확인하고 임의 부분은 다른 실행에서 다른 솔루션을 생성 할 수 있으며 평균적으로 더 나은 성능을 제공합니다.
추신. 이 모든 것 (및 기타)은 순수 JavaScript (병렬 처리 및 WYSIWYG 포함) 기능으로 구현되었습니다.
PS2. 알고리즘은 동시에 하나 이상의 (다른) 솔루션을 생성하기 위해 쉽게 병렬화 될 수 있습니다.
도움이 되었기를 바랍니다
무작위 확률 적 접근 방식을 사용하여 시작하지 않는 이유는 무엇입니까? 단어로 시작한 다음 무작위 단어를 반복해서 골라 크기 등에 대한 제약을 깨지 않고 퍼즐의 현재 상태에 맞추려고합니다. 실패하면 다시 시작하십시오.
이와 같은 Monte Carlo 접근 방식이 얼마나 자주 작동하는지 놀랄 것입니다.
다음은 nickf의 답변과 Bryan의 Python 코드를 기반으로 한 JavaScript 코드입니다. 다른 사람이 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();
}
길이와 스크래블 점수의 두 가지 숫자를 생성합니다. 스크래블 점수가 낮 으면 참여하기가 더 쉽다는 것을 의미한다고 가정합니다 (낮은 점수 = 많은 공통 문자). 목록을 내림차순으로 정렬하고 스크래블 점수를 오름차순으로 정렬합니다.
다음으로 목록 아래로 이동하십시오. 단어가 기존 단어와 교차하지 않는 경우 (각 단어의 길이와 스크래블 점수로 각 단어를 확인) 대기열에 넣고 다음 단어를 확인합니다.
헹구고 반복하면 십자말 풀이가 생성됩니다.
물론, 이것이 O (n!)이고 십자말 풀이를 완성하는 것이 보장되지는 않지만 누군가가 그것을 향상시킬 수 있다고 확신합니다.
이 문제에 대해 생각하고 있습니다. 제 생각에는 정말 조밀 한 십자말 풀이를 만들려면 제한된 단어 목록이 충분할 것이라고 기대할 수 없습니다. 따라서 사전을 가져 와서 "trie"데이터 구조에 배치 할 수 있습니다. 이렇게하면 남은 공백을 채우는 단어를 쉽게 찾을 수 있습니다. trie에서는 "c? t"형식의 모든 단어를 제공하는 순회를 구현하는 것이 상당히 효율적입니다.
그래서, 제 일반적인 생각은 : 여기에 설명 된 것과 같이 비교적 무차별 한 접근 방식을 만들어 저밀도 크로스를 만들고 공백을 사전 단어로 채 웁니다.
다른 사람이이 접근 방식을 취했다면 알려주세요.
나는 십자말 풀이 생성기 엔진을 가지고 놀고 있었고 이것이 가장 중요하다는 것을 알았습니다.
0.!/usr/bin/python
ㅏ. allwords.sort(key=len, reverse=True)
비. 나중에 임의의 선택으로 반복하지 않는 한 쉬운 방향을 위해 매트릭스 주위를 돌아 다니는 커서와 같은 항목 / 객체를 만드십시오.
첫 번째는 첫 번째 쌍을 집어 0,0에서 가로 질러 놓는다. 첫 번째 단어를 현재 크로스 워드 '리더'로 저장합니다.
대각선 확률이 더 큰 다음 빈 셀로 커서를 대각선 또는 무작위 순서로 이동
like 단어를 반복하고 여유 공간 길이를 사용하여 최대 단어 길이를 정의하십시오.
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 )
내가 사용한 여유 공간과 단어를 비교하려면 다음을 사용하십시오.
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
성공적으로 사용한 각 단어 후에 방향을 변경하십시오. 모든 셀이 채워지는 동안 반복하거나 단어가 부족하거나 반복 제한에 의해 다음을 수행하십시오.
# CHANGE ALL WORDS LIST
inexOf1stWord = allwords.index( leading_w )
allwords = allwords[:inexOf1stWord+1][:] + allwords[inexOf1stWord+1:][:]
... 새 낱말 맞추기를 다시 반복합니다.
채우기의 용이성과 일부 추정 계산으로 채점 시스템을 만드십시오. 현재 십자말 풀이에 점수를 부여하고 점수 시스템이 점수를 만족하는 경우 만든 십자말 풀이 목록에 추가하여 나중에 선택 범위를 좁 힙니다.
첫 번째 반복 세션 후 만들어진 십자말 풀이 목록에서 다시 반복하여 작업을 완료합니다.
더 많은 매개 변수를 사용하면 속도가 크게 향상 될 수 있습니다.
가능한 십자가를 알기 위해 각 단어가 사용하는 각 문자의 색인을 얻습니다. 그런 다음 가장 큰 단어를 선택하여 기본으로 사용합니다. 다음 큰 것을 선택하고 교차하십시오. 헹구고 반복하십시오. 아마도 NP 문제 일 것입니다.
또 다른 아이디어는 강도 메트릭이 그리드에 얼마나 많은 단어를 넣을 수 있는지에 대한 유전 알고리즘을 만드는 것입니다.
내가 찾는 어려운 부분은 특정 목록을 넘을 수 없다는 것을 알 때입니다.
이것은 AI CS50 과정 의 프로젝트로 나타납니다. Harvard . 이 아이디어는 십자말 풀이 문제를 제약 만족 문제로 공식화하고 검색 공간을 줄이기 위해 다른 휴리스틱으로 역 추적하여 해결하는 것입니다.
시작하려면 몇 가지 입력 파일이 필요합니다.
`
###_####_#
____####_#
_##_#_____
_##_#_##_#
______####
#_###_####
#_##______
#_###_##_#
_____###_#
#_######_#
##_______#
`
후보 단어가 선택 될 입력 어휘 (단어 목록 / 사전) (다음에 표시된 것과 같음).
a
abandon
ability
able
abortion
about
above
abroad
absence
absolute
absolutely
...
이제 CSP가 정의되고 다음과 같이 해결됩니다.
다음은 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█
`
다음 애니메이션은 역 추적 단계를 보여줍니다.
다음은 벵골어 (벵골어) 단어 목록이있는 또 다른 단어입니다.
이 문제에 대한 JavaScript / jQuery 솔루션을 코딩했습니다.
샘플 데모 : http://www.earthfluent.com/crossword-puzzle-demo.html
소스 코드 : https://github.com/HoldOffHunger/jquery-crossword-puzzle-generator
내가 사용한 알고리즘의 의도 :
내가 사용한 알고리즘을 설명하겠습니다.
공통 문자를 공유하는 단어에 따라 단어를 그룹화하십시오.
이러한 그룹에서 새로운 데이터 구조 ( "단어 블록")의 집합을 작성합니다.이 단어는 기본 단어 (다른 모든 단어를 통해 실행 됨)와 다른 단어 (기본 단어를 통해 실행 됨) 인 것입니다.
십자말 풀이의 맨 위 왼쪽 위치에있는 첫 번째 단어 블록으로 십자말 풀이를 시작하십시오.
나머지 단어 블록의 경우 십자말 풀이의 가장 오른쪽 아래에서 시작하여 채울 수있는 슬롯이 더 이상 없을 때까지 위쪽과 왼쪽으로 이동합니다. 왼쪽보다 위쪽에 빈 열이 더 많은 경우 위쪽으로 이동하고 그 반대의 경우도 마찬가지입니다.
var crosswords = generateCrosswordBlockSources(puzzlewords);
. 이 값을 콘솔에 기록하십시오. 잊지 마세요. 게임에는 "치트 모드"가 있습니다. 여기서 "Reveal Answer"를 클릭하면 즉시 값을 얻을 수 있습니다.