bool SolveCompleteDescriptionLines(bool firstCall, int num, List <Line[, ]> linesOnFaces, List <Line> completeLines, MyPuzzle myPuzzle, LineHeap Q) { // num개의 complete-description 라인을 푼다 for (int i = 0; i < Mathf.Min(num, completeLines.Count); ++i) { // solved 아닌 라인을 찾는다 Line lineToSolve = FindUnSolvedCompleteLine(completeLines, myPuzzle); if (lineToSolve == null) { return(firstCall); // unsolved인 complete-description이 하나도 없음. first call이 아니라면 false 반환 } // line solver로 라인을 푼다 List <CELLSTATE> beforeLineSolver = GetBeforeLine(lineToSolve.line, myPuzzle); List <CELLSTATE> afterLineSolver = lineSolver.lineSolver(lineToSolve.clue, lineToSolve.line, myPuzzle); lineToSolve.mark = true; lineToSolve.solved = true; // line solver 적용 전, 후의 라인을 비교한다 int lineLen = afterLineSolver.Count; List <puzzleIndex> line = lineToSolve.line; for (int j = 0; j < lineLen; ++j) { if (beforeLineSolver[j] != afterLineSolver[j]) { // cell state 영구적으로 변경하기 myPuzzle.puzzleState[line[j].z, line[j].y, line[j].x] = afterLineSolver[j]; if (afterLineSolver[j] == CELLSTATE.EMPTY) { #if SolveTheActualPuzzle puzzleCube[line[j].z, line[j].y, line[j].x].PlayDestoryEffect(); #endif myPuzzle.myBreaks++; } else if (afterLineSolver[j] == CELLSTATE.SOLID) { #if SolveTheActualPuzzle puzzleCube[line[j].z, line[j].y, line[j].x].isProtected = true; puzzleCube[line[j].z, line[j].y, line[j].x].SetColor(Color.cyan, (int)Cube.MATERIAL_INDEX.BACKGROUND); puzzleCube[line[j].z, line[j].y, line[j].x].PlayProtectAnimation(); #endif } // 수직 라인의 우선순위 높이기 Line[] perpendicularLines = GetPerpendicularLines(linesOnFaces, lineToSolve.faceType, line[j]); if (afterLineSolver[j] == CELLSTATE.SOLID) { perpendicularLines[0].priority += 2; perpendicularLines[1].priority += 2; } else if (afterLineSolver[j] == CELLSTATE.EMPTY) { perpendicularLines[0].priority += 1; perpendicularLines[1].priority += 1; } // 수직 라인들 힙에 넣기 for (int index = 0; index < 2; ++index) { if (!perpendicularLines[index].insideHeap) { Q.Insert(perpendicularLines[index]); } } } } } return(true); }
bool SolvePuzzle(Puzzle puzzle, List <Line[, ]> linesOnFaces, List <Line> completeLines) { MyPuzzle myPuzzle = new MyPuzzle(puzzle); LineHeap Q = new LineHeap(puzzle.zLen * puzzle.yLen * puzzle.xLen); // line solver가 사용 할 우선순위 큐 // 처음 몇 개의 complete-description 라인을 마크하고 풀고, 상태가 바뀐 cell의 수직 라인들 우선순위 올리고 힙에 넣기 SolveCompleteDescriptionLines(true, 3, linesOnFaces, completeLines, myPuzzle, Q); // complete-description 라인들만 빼고 모두 힙에 넣는다 foreach (var face in linesOnFaces) { foreach (Line line in face) { if (!line.isCompleteDescription && !line.insideHeap) { Q.Insert(line); } } } // 휴리스틱 알고리즘으로 풀기... while (myPuzzle.myBreaks != puzzle.breakCount) { Line lineToSolve = Q.Pop(); if (lineToSolve == null) { // 풀다가 안 풀리면 complete-description 라인 하나씩 추가 해 주면서 진행 if (SolveCompleteDescriptionLines(false, 1, linesOnFaces, completeLines, myPuzzle, Q)) { continue; } else { Debug.Log("퍼즐 풀이 실패!"); return(false); // 퍼즐 풀이 실패! } } if (isLinePassivelySolved(lineToSolve, myPuzzle)) { lineToSolve.solved = true; // 이미 풀려있는 라인임 continue; } // line solver로 라인을 푼다 List <CELLSTATE> beforeLineSolver = GetBeforeLine(lineToSolve.line, myPuzzle); List <CELLSTATE> afterLineSolver = lineSolver.lineSolver(lineToSolve.clue, lineToSolve.line, myPuzzle); // line solver 적용 전, 후의 라인을 비교한다 int lineLen = afterLineSolver.Count; List <puzzleIndex> line = lineToSolve.line; for (int j = 0; j < lineLen; ++j) { if (beforeLineSolver[j] != afterLineSolver[j]) { lineToSolve.mark = true; // deduction을 하나라도 만들었으므로 라인을 mark한다 // cell state 영구적으로 변경하기 myPuzzle.puzzleState[line[j].z, line[j].y, line[j].x] = afterLineSolver[j]; if (afterLineSolver[j] == CELLSTATE.EMPTY) { #if SolveTheActualPuzzle puzzleCube[line[j].z, line[j].y, line[j].x].PlayDestoryEffect(); #endif myPuzzle.myBreaks++; } else if (afterLineSolver[j] == CELLSTATE.SOLID) { #if SolveTheActualPuzzle puzzleCube[line[j].z, line[j].y, line[j].x].isProtected = true; puzzleCube[line[j].z, line[j].y, line[j].x].SetColor(Color.cyan, (int)Cube.MATERIAL_INDEX.BACKGROUND); puzzleCube[line[j].z, line[j].y, line[j].x].PlayProtectAnimation(); #endif } // 수직 라인과 현재 라인의 우선순위 수정 Line[] perpendicularLines = GetPerpendicularLines(linesOnFaces, lineToSolve.faceType, line[j]); if (afterLineSolver[j] == CELLSTATE.SOLID) { lineToSolve.priority -= 2; perpendicularLines[0].priority += 2; perpendicularLines[1].priority += 2; } else if (afterLineSolver[j] == CELLSTATE.EMPTY) { lineToSolve.priority -= 1; perpendicularLines[0].priority += 1; perpendicularLines[1].priority += 1; } // 수직 라인들 힙에 넣기 for (int index = 0; index < 2; ++index) { if (!perpendicularLines[index].insideHeap) { Q.Insert(perpendicularLines[index]); } else { // 이미 힙에 있다면 float up 해 주기 Q.Floatup(perpendicularLines[index].heapIndex); } } } } // line이 풀렸는가? if (isLineSolved(lineToSolve, myPuzzle)) { lineToSolve.solved = true; } } // 성공! return(true); }