Ejemplo n.º 1
0
    Line FindUnSolvedCompleteLine(List <Line> completeLines, MyPuzzle myPuzzle)
    {
        Line lineToSolve = null;

        // completeLines 리스트의 맨 앞에서부터 라인을 뽑아 solved인지 검사
        // 맨 앞에서부터 뽑는 이유는 number clue의 숫자가 0이 아닌 라인들을 리스트 앞쪽에 몰아서 저장 해 놓았기 때문
        while (completeLines.Count > 0)
        {
            lineToSolve = completeLines[0];

            if (isLineSolved(lineToSolve, myPuzzle))
            {
                lineToSolve.solved = true;
                completeLines.RemoveAt(0);

                lineToSolve = null;
            }
            else
            {
                break;
            }
        }

        return(lineToSolve);
    }
Ejemplo n.º 2
0
        public static void Puzzle_Solve_Returns_Correct_Value_Based_On_Args_Length()
        {
            // Arrange
            string[] args = new[] { "1" };
            var target = new MyPuzzle(2);

            // Act
            int actual = target.Solve(args);

            // Assert
            Assert.Equal(-1, actual);

            // Arrange
            args = new string[0];
            target = new MyPuzzle(1);

            // Act
            actual = target.Solve(args);

            // Assert
            Assert.Equal(-1, actual);

            // Arrange
            target = new MyPuzzle(0);

            // Act
            actual = target.Solve(args);

            // Assert
            Assert.Equal(0, actual);
            Assert.Equal(42, target.Answer);
        }
Ejemplo n.º 3
0
    public async Task Puzzle_Solve_Returns_Correct_Value_Based_On_Args_Length()
    {
        // Arrange
        string[] args = new[] { "1" };
        var      cancellationToken = CancellationToken.None;

        var target = new MyPuzzle(2)
        {
            Logger = Logger,
        };

        // Act and Assert
        await Assert.ThrowsAsync <PuzzleException>(() => target.SolveAsync(args, cancellationToken));

        // Arrange
        args   = Array.Empty <string>();
        target = new MyPuzzle(1);

        // Act and Assert
        await Assert.ThrowsAsync <PuzzleException>(() => target.SolveAsync(args, cancellationToken));

        // Arrange
        target = new MyPuzzle(0);

        // Act
        PuzzleResult actual = await target.SolveAsync(args, cancellationToken);

        // Assert
        actual.ShouldNotBeNull();
        actual.Solutions.ShouldNotBeNull();
        actual.Solutions.Count.ShouldBe(1);
        actual.Solutions[0].ShouldBe(42);
        target.Answer.ShouldBe(42);
    }
Ejemplo n.º 4
0
    bool isLineSolved(Line line, MyPuzzle myPuzzle)
    {
        int solids    = 0;
        int lineCount = line.line.Count;
        var l         = line.line;

        for (int i = 0; i < lineCount; ++i)
        {
            CELLSTATE currentCell = myPuzzle.puzzleState[l[i].z, l[i].y, l[i].x];

            // 하나라도 BLANK인 큐브가 있으면 라인 전체가 풀리지 않은 것
            // 즉, 이 라인이 풀린다면 최소 하나의 단서가 추가될 수 있음
            if (currentCell == CELLSTATE.BLANK)
            {
                return(false);
            }

            if (currentCell == CELLSTATE.SOLID)
            {
                solids++;
            }
        }

        return(solids == line.clue.number);  // SOLID가 아닌 큐브는 모두 EMPTY일 것이므로 group은 상관 없이 전체 SOLID 갯수만 보면 된다
    }
Ejemplo n.º 5
0
    List <CELLSTATE> GetBeforeLine(List <puzzleIndex> line, MyPuzzle myPuzzle)
    {
        int lineLen = line.Count;

        List <CELLSTATE> res = new List <CELLSTATE>(lineLen);

        for (int i = 0; i < lineLen; ++i)
        {
            res.Add(myPuzzle.puzzleState[line[i].z, line[i].y, line[i].x]);
        }

        return(res);
    }
Ejemplo n.º 6
0
    bool isLinePassivelySolved(Line line, MyPuzzle myPuzzle)
    {
        int solids    = 0;
        int lineCount = line.line.Count;
        var l         = line.line;

        for (int i = 0; i < lineCount; ++i)
        {
            CELLSTATE currentCell = myPuzzle.puzzleState[l[i].z, l[i].y, l[i].x];

            // 모든 solid를 칠하지 않았어도 empty만 맞으면 풀었다고 본다
            if (currentCell == CELLSTATE.SOLID || currentCell == CELLSTATE.BLANK)
            {
                solids++;
            }
        }

        return(solids == line.clue.number);  // SOLID 또는 BLANK가 아닌 큐브는 모두 EMPTY일 것이므로 group은 상관 없이 전체 SOLID 갯수만 보면 된다
    }
Ejemplo n.º 7
0
        public static void Puzzle_Returns_Minus_One_If_Too_Few_Arguments()
        {
            // Arrange
            string[] args   = new[] { "1" };
            Puzzle   target = new MyPuzzle(2);

            // Act
            int actual = target.Solve(args);

            // Assert
            Assert.Equal(-1, actual);

            // Arrange
            args   = Array.Empty <string>();
            target = new MyPuzzle(1);

            // Act
            actual = target.Solve(args);

            // Assert
            Assert.Equal(-1, actual);
        }
Ejemplo n.º 8
0
    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);
    }
Ejemplo n.º 9
0
    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);
    }