https://www.acmicpc.net/problem/3300

 

3300번: 무어 기계

문제 무어 기계는 상태에 의해서 출력이 결정되는 유한 상태 기계이다. 무어 기계는 이름은 미국의 수학자이자 컴퓨터 과학자 Edward F. Moore의 이름을 따서 지었다. 무어 기계의 상태 전이는 입력�

www.acmicpc.net

무어 머신을 입력받고 이를 그래프로 표현하는 부분을 구상하였습니다.

원래는 이대로 끝내려 했지만 4일에 한번 있는 저녁 운동 없는 날이기도 하고,
요즘 알고리즘을 열심히 안한 느낌도 있어서 구현까지 해보았습니다.

테스트 결과 왠만한 경우에서 잘 작동하고 있습니다.

 

더보기
#include <iostream>
#include <sstream>
#include <string>   
#include <stack>
#include <vector>
#include <algorithm>

using namespace std;

int FindFormerNode(const vector<int>& Heights, const int& height, const int& index)
{
    for (int i = index - 1; i >= 0; --i)
    {
        if (Heights[i] < height)
        {
            return i;
        }
    }
    return 0;
}

vector<int> FindFormerNodes(const vector<vector<int>>& Nodes, const vector<int>& Heights, const int& height, const int& index, const int& gap)
{
    vector<int> ret;
    for (int i = index - 1; i >= 0; --i)
    {
        if (height == Heights[i])
        {
            break;;
        }
        if (!Nodes[i].empty())
        {
            continue;
        }
        if (height - Heights[i] >= gap)
        {
            ret.push_back(i);
        }
    }
    return ret;
}

int main()
{
    int testtime = 0;
    
    cin >> testtime;

    while (testtime-- > 0)
    {
        string machine, testcase;

        char result = '!', special = 0;
        int gap = 0, height = 0, index = 0;

        vector<char> Spells;
        vector<int> Indices, Heights;
        vector<vector<int>> Nodes;

        cin >> machine >> testcase;

        for (const auto& iter : machine)
        {
            if (iter == '(')
            {
                ++height;
                ++gap;
                special = iter;
                continue;
            }
            if (iter == ')')
            {
                --height;
                --gap;
                special = iter;
                continue;
            }
            if (iter == '|')
            {
                special = iter;
                continue;
            }
            switch (special)
            {
            case '|':
            {
                Spells.push_back(iter);
                Heights.push_back(height);
                Nodes.push_back(vector<int>());
                Nodes[FindFormerNode(Heights, height, index)].push_back(index);
                Indices.push_back(index++);
            }
                break;
            case ')':
            {
                Spells.push_back(iter);
                Heights.push_back(height);
                Nodes.push_back(vector<int>());
                auto Former = FindFormerNodes(Nodes, Heights, height, index, gap);
                for (const auto& i : Former)
                {
                    Nodes[i].push_back(index);
                }
                Indices.push_back(index++);
                gap = 0;
            }
                break;
            default:
            {
                Spells.push_back(iter);
                Heights.push_back(height);
                Nodes.push_back(vector<int>());
                if (Nodes.size() > 1)
                {
                    Nodes[index - 1].push_back(index);
                }
                Indices.push_back(index++);
                gap = 0;
            }
                break;
            }
            special = iter;
        }
        cout << endl;

        
        cout << result << endl;
    }
    return 0;
}

 

우선 Special Character 처리입니다.

흐름에 영향을 주는 Character는 (, |, ) 3가지가 있습니다.

 

( 문자는 입력 시 현재 높이와 이전 Node로부터의 높이가 1씩 올라갑니다.

 

) 문자는 반대로 입력 시 현재 높이와 이전 Node로부터의 높이가 1씩 내려갑니다.

 

| 문자는 입력 자체로는 영향이 없습니다.

 

이 특수 문자는 자체가 입력될 때는 아무 행동도 하지 않습니다.

그렇기에 Special이라는 메모리에 따로 저장을 합니다.

 

그리고 위 3 문자가 아닌 다른 문자 입력 시 Special 문자에 따라 흐름이 달라지게 됩니다.

 

( 문자 입력 시에는 앞 Node에 자신의 Index를 추가합니다.

하지만 이는 일반 문자도 동일하기에 따로 처리하지 않고 있습니다.

 

| 문자는 입력 시 자신의 선행 Node를 찾아 그 Node에 Index를 추가합니다.

선행 Node는 자신의 앞에 있는 Index 중 자신의 높이보다 낮은 높이를 지닌 첫번째 Node입니다.

 

) 문자는 입력 시 자신의 선행 Node을 찾아 그 Node에 Index를 추가합니다.

이 때 선행 Node는 | 문자와 다르게 검색합니다.

 

| 문자는 선행 Node가 자기 자신과 연속적으로 붙어 있기에 단순 비교로 가능합니다.

하지만 )문자는 모든 Node를 검색해야 하기에 단순 비교로는 다른 군(| 문자 앞의 문자들)을 구분할 수 없게 됩니다.

이 때문에 이전 Node와의 거리인 gap이 추가된 것입니다.

 

먼저 비교 Node와 자신의 높이가 동일하다면 함수를 마칩니다.

이는 이미 ) 범위 안의 연산이 끝났다는 것을 의미하기 때문입니다.

 

또한 비교 Node가 이미 다른 Index 값을 가지고 있으면 다음 연산으로 넘어갑니다.

) 연산은 연속적인 Node의 리스트 중 꼬리에만 붙습니다.

Node가 이미 다른 Index 값을 가지고 있다면 이는 꼬리가 아니라는 의미입니다.

 

위 두가지 경우를 제외하고, 두 Node의 높이 차가 gap보다 크거나 같을 경우 Return 할 Vector에 값을 추가합니다.

 

여기서 중요한 조건은 두번째입니다.

그 이유는 세번째 조건은 같은 군에서 이미 Node 값이 있는 것들도 옳다고 판단하기 때문입니다.

그리고 그 조건을 먼저 제거해주는 것이 두번째 조건입니다.

 

세번째 조건은 금새 다다를 수 있지만, 두번째 조건이 없다면 한참을 해맬 것입니다.

 

여기까지 짜놓고 끝까지 작성하지 않은 이유는
아직 이 Graph를 효율적으로 순회해서 탐색하는 방법은 명확하게 정하지 못했기 때문입니다.

일단 시도해보는 방법도 있지만,
이 문제를 풀어오면서 생각을 오래해서 정리를 하는 것이 좋은 답을 도출한다는 것을 느꼈습니다.

이 느낌을 더 느껴보고 싶습니다.

 

그래서 내일 Graph 순회 방법을 고려해보고자 합니다.

일단 가지고 있는 아이디어는 Spells 길이와 동일한 Vector<int>iterator를 두는 것입니다.

Graph를 한 방향으로만 가는 것이 아니라 다른 방향으로도 순회해야 하는데,

재귀문은 메모리 문제가 발생할 것 같기에 이런 방법을 사용해야 할 것 같습니다.

 

더보기
        cout << "Indices:\t";
        for (const auto& i : Indices)
        {
            cout << i << " ";
        }
        cout << endl;
        cout << "Spells:\t\t";
        for (const auto& i : Spells)
        {
            cout << i << " ";
        }
        cout << endl;
        cout << "Heights:\t";
        for (const auto& i : Heights)
        {
            cout << i << " ";
        }
        cout << endl;
        for (const auto& i : Nodes)
        {
            for (const auto& j : i)
            {
                cout << j << " ";
            }
            cout << endl;
        }

 

https://www.acmicpc.net/problem/3300

 

3300번: 무어 기계

문제 무어 기계는 상태에 의해서 출력이 결정되는 유한 상태 기계이다. 무어 기계는 이름은 미국의 수학자이자 컴퓨터 과학자 Edward F. Moore의 이름을 따서 지었다. 무어 기계의 상태 전이는 입력�

www.acmicpc.net

대략적인 가닥은 잡혔는데 핵심적인 부분의 방식을 아직 구상하지 못하고 있습니다.

우선 대략적인 부분.

입력된 무어 머신을 앞에서부터 순회합니다.

그리고 Spells Vector와 Index Vector로 나누어서 관리합니다.

Spells는 무어머신에 입력된 Alphabat. 즉 State의 저장입니다.

중복된 알파뱃이 입력될 수 있으므로 이를 따로 관리하기 위해 Vector로 관리를 합니다.

 

그리고 이 State들간의 관계. 즉 Condition을 최대한 가볍게 구현하기 위해 Vector<Vector<int>>를 추가했습니다.

이 Vector는 Index값으로 State들간의 관계를 나타냅니다.

선행 State와 후행 State가 일대다 관계도, 다대일 관계도 가능하기에
어쩔 수 없이 이차원 Vector를 사용해야 할 것 같습니다.

 

무어 머신을 순회하면서 Alphabat이 들어오면 기본적으로 선행 Alphabat의 Index Vector에 값이 추가됩니다.

단, Alphabat이 아닌 특수 문자가 들어올 때에는 특수한 작업이 발생합니다.

이 때 문제가 발생합니다.

 

) 문자가 들어온 이후의 첫 Alphabat은 선행 State들과 연결을 지어야 합니다.

이것이 가능하려면 선행 Alphabat이 어디부터 어디까지인지 판단을 할 수 있어야 합니다.

그런데 이를 판단하는 방식을 아직 구상하지 못했습니다.

 

우선은 height Vector를 먼저 도입해볼까 합니다.

현재 Alphabat과 인접한 Alphabat과 같은 Height를 지닌 연속적인 모든 Alphabat을

선행 Alphabat으로 판정하는 것입니다.

 

그 이후에 형성된 그래프를 순회하는 것도 고민을 해봐야 할 것 같습니다.

오늘은 크게 무언가를 하지는 않고 간단하게 구현만 조금 했습니다.

우선 어제 새벽에 작성한 내용들을 담을 수 있는 Interface를 새로 선언했습니다.

가능하면 함수 선언도 해두려고 했으나 기능 구현에 대해
포괄적으로 적용 할 수 있는 형태를 아직 확립하지 못해 몇몇 부분을 보류하고 있습니다.

 

그리고 일전에 선언 해두었던 InRangeCharacter와 IObjectActivity Interface를 ActorBase와 Character에 적용하였습니다.

아무래도 내용이 많다보니 이 부분이 의외로 시간을 꽤 잡아먹었습니다.

 

마지막으로 Melee Attack의 판정을 개선했습니다.

Notify 발생 위치를 조절하고,
왼손 오른손을 햇갈려 했던 것을 명확히 하여 수정한 결과 비교적 만족스럽게 작동하고 있습니다.

 

가능하면 FSM을 관리하는 Sequential Interface도 미리 선언해두고 싶으나, 시간이 오래 걸릴 것 같습니다.

저번처럼 밤에 생각이 나면 맞춰보겠지만, 우선은 후순위로 미뤄두려고 합니다.

일단은 CheckAnswer 부분을 구현을 해볼까 합니다.

현재 생각나는 것은 물건 부착, 피격, Widget에 텍스트 입력을 생각하고 있습니다.

 

그 다음에는 Climb 기능을 먼저 구현하고자 합니다.

Trap보다는 아무래도 이미 완성된 기능들이다 보니 Climb를 먼저 정리하는 편이 더 적절하다고 생각합니다.

하면서 생각이 뚫리면 장애물을 넘어가는 기능도 조심스래 건드려볼 계획입니다.

 

그 다음에 Trap을 구현하고, Puzzle을 제작해보려 합니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.07.30 개발일지  (0) 2020.07.30
20.07.29 개발일지  (0) 2020.07.29
20.07.26 개발일지  (0) 2020.07.26
20.07.23 개발일지  (0) 2020.07.23
20.07.22 개발일지  (0) 2020.07.22

https://www.acmicpc.net/problem/3300

 

3300번: 무어 기계

문제 무어 기계는 상태에 의해서 출력이 결정되는 유한 상태 기계이다. 무어 기계는 이름은 미국의 수학자이자 컴퓨터 과학자 Edward F. Moore의 이름을 따서 지었다. 무어 기계의 상태 전이는 입력�

www.acmicpc.net

이전에 짰던 코드가 입력되는 String에 반복적인 알파뱃이 존재 할 때
Shift 과정에서 오류가 발생한다는 점을 깨달았습니다.

더 큰 문제는 추가적인 복잡한 구조나 대량의 메모리가 없이는 개선을 할 수 없다는 점이었습니다.

 

그래서 구조를 다시 짜보고 있습니다.

처음에는 struct를 이용한 Graph를 이용해보려 했습니다.

runtime 상에서 메모리를 잡는 거은 큰 부하가 걸리므로 Stack Object들로 관리를 하였습니다.

하지만 Graph라고 해도 Directed Graph라서 흐름을 역행하는 케이스를 검색하는 것에 문제가 발생하였습니다.

 

현재는 여기까지 개발을 한 상태입니다.

그리고 일지를 적다가 Graph를 Array로 표현하는 방법을 떠올렸습니다.

입력된 Machine에서 모든 State를 Vector에 순차적을로 담고,
각 State가 어떤 State로 갈 수 있는지 index 값을 Vector로 담고 있는 방식입니다.

 

여기서 문제가 될 수 있는 부분은 두가지입니다.

하나는 하향식이든 상향식이든 일대 다 관계가 발생할 수 있다는 점.

다른 하나는 State가 많아질수록 기하급수적으로 Graph 정보가 많아진다는 점입니다.

 

우선은 이 구현하던 것을 지우고 Array 식으로 변경해서 새로 구현을 해보려 합니다.

아무래도 Vector보다는 부하가 적을 것 같아서입니다.

조금 조급해져 주말에 개발하려다가 지원을 고민하는 회사에 지원하기로 마음 먹어 야밤에 글을 씁니다.

 

Trap과 Puzzle의 관계에 대해서 고민을 해보았습니다.

 

제가 개발하는 게임 내에서

Trap이란 유저의 진행을 방해하는 모든 장치들을 지칭합니다.

단순히 텔레포트, 총알 발사, 몬스터 소환, 방해물 설치서부터 시작해 여러 장치들이 복잡하게 발동되기도 합니다.

 

Puzzle이란 유저의 행동으로 해제가 가능한 모든 장치들을 지칭합니다.

Puzzle은 대체로 Trap을 동반하지만, 단순한 입력도 게임 내에서는 Puzzle로 취급됩니다.

 

Trap은 여러 Trap이 복합적으로 발동되더라도 기본적으로 각각의 Trap의 발동 조건이 변하지 않습니다.

하지만 Puzzle이 지니고 있는 Trap은 발동 조건이 바뀔 수도 있습니다.

예를 들어, 범위 안에 플레이어가 존재해야 발동하는 Trap의 경우,
Puzzle에 붙으면 그 범위가 Puzzle의 범위에서 발동해야 할 수도 있습니다.

이전 구조에서도 이 문제를 고민하면서 변경을 꾀했는데 현재 이에 대해 두가지 방안을 고민 중입니다.

 

1. 이 문제는 모든 Trap과 Puzzle의 SuperClass인 THActorBase가 태생적으로 Area를 지니고 있기 때문이다.

그러니 Puzzle에서 Trap들의 Area를 자신의 Area와 동일하게 지정하여
동일한 이벤트가 발생하는 대상을 Trap의 Area에서 Puzzle의 Area로 바꾸어 보려 합니다. 

이 방법의 가장 큰 장점은 나중에 기획을 하면서

Multiple Trap에서 Trap의 발동 범위가 바뀌는 경우에도 문제 없이 적용이 가능하다는 점입니다.

단점? 아니 문제점은 정상적으로 작동할지 아직 모릅니다.

 

2. Puzzle이 가지고 있는 Trap의 이벤트 함수를 모두 지운 뒤, 자체적으로 생성해서 다시 배정하는 방식입니다.

이는 구조 변경 전에도 동일하게 적용될 수 있는 방법이라 1번 방안에 비해 비교적 적절치 못하다고 생각합니다.

 

Trap과 Puzzle의 관계를 배정하면서 몇가지 추가되어야 하는 Interface가 있습니다.

첫번째로, FSM을 관리하는 Interface가 필요합니다.

여러 Trap들이 발동 할 때, 순차적으로 발동한다는 보장이 없습니다.

지름길이 있을 수도 있고, 실패 시 다른 Sequence로 움직여야 할 수 있습니다.

그리고 이러한 흐름을 관리하려면 FSM이 필요할 것이라 봅니다.

더불어서 FSM을 효과적으로 표현할 수 있는 자료구조 방식도 구상해야 합니다.

 

두번째로, CheckAnswer Interface를 다시 되살려야 합니다.

Puzzle의 복합적인 작동을 고려하다 보니 Check Answer 과정도 유일하다고 볼 수 없습니다.

때문에 Check Answer를 하는 Object를 종류별로 제작하고, Puzzle이 상황에 맞게 1개 이상 소유하도록 하려 합니다.

 

우선은 각 Interface들을 제작하고, Trap에 적용을 한 뒤 Trap과 Puzzle을 더 기획하려 합니다.

가능하면 Check Answer Object을 3가지 정도 만들고, Trap과 조합하여 여러 종류의 Puzzle을 생성해보려 합니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.07.29 개발일지  (0) 2020.07.29
20.07.27 개발일지  (0) 2020.07.27
20.07.23 개발일지  (0) 2020.07.23
20.07.22 개발일지  (0) 2020.07.22
20.07.20 개발일지  (0) 2020.07.20

https://www.acmicpc.net/problem/3300

 

3300번: 무어 기계

문제 무어 기계는 상태에 의해서 출력이 결정되는 유한 상태 기계이다. 무어 기계는 이름은 미국의 수학자이자 컴퓨터 과학자 Edward F. Moore의 이름을 따서 지었다. 무어 기계의 상태 전이는 입력�

www.acmicpc.net

예시는 다 통과가 될 정도로 수정을 하고 제출을 해보았는데 런타임 에러가 발생했습니다.

몇가지 예시를 종이에 더 적어보았는데 이걸 확대해서 테스트 해봐야겠습니다.

썩 마음에 드는 코드 방식도 아닌데 이게 문제가 생기니까 막막합니다.

이거 언제 또 고치지...

오늘 Unreal 개발 하면서 너무 정신이 말렸는지 멀미가 나기 시작해 쉽니다.

'개발일지' 카테고리의 다른 글

20.08.12 비개발일지  (0) 2020.08.12
20.08.11 비개발일지  (0) 2020.08.11
20.07.22 비개발일지  (0) 2020.07.22
20.07.21 비개발일지  (0) 2020.07.21
20.07.17 비개발일지  (0) 2020.07.17

오늘은 하루를 꼬박 투자해 Melee Attack에 Interface를 적용하였습니다.

 

구현 방식은 다음과 같습니다.

1. Melee Attack 키를 누를 시 LayeredMotion 트리거가 켜집니다.

2. Animation FSM에서 동기화 된 LayeredMotion 트리거에 따라 Melee Attack Montage가 재생됩니다.

3. Montage가 재생되자마자 Left Hand HitBox의 Hit 판정 트리거가 켜집니다.

4. Montage의 적절한 중간 지점에서 Left Hand HitBox의 Hit 판정 트리거가 꺼지고,
한번 충돌 판정 난 객체들을 저장하는 Buffer가 비워집니다.

5. 3과 4사이에 HitBox가 무언가와 Collision이 발생했을 경우 HitBox의 Collision 이벤트가 발생합니다.

6. Collision 대상이 자기 자신(Character)와 다르고,
동일한 Melee Attack 모션 중 한번도 Collision이 발생하지 않았으며,
현재 Melee Attack 중일 경우 데미지를 계산합니다.

 

왜 처음 생각하고 개발 할 때는 엄청나게 복잡했는데 다 만들고 정리하니까 이리도 간단한지 모르겠습니다.

이런걸 이렇게 시간 들여서 만들었나 자괴감 들고 괴롭습니다.

이렇게나 못했다니...

 

Hit 판정 다음에는 Trap에 대한 것인데 여기에 대해 크나큰 고민이 있습니다.

 

Trap과 Puzzle의 관계에 대해서는 처음 구조를 구상했을 때부터 고민이 많았습니다.

우선 기본적으로 두 Object 모두 범위 판정이 필요하기에 THObjectBase를 만들고,
THTrapBase, THPuzzleBase는 이를 기반으로 만들어졌습니다.

그리고 Puzzle은 대체로 Trap 성분도 지니고 있기에 Puzzle이 Trap을 지니고 있어야 합니다.

 

여기서 Trap을 상속 받는 방법과 Trap을 Component로 가지고 있는 방법이 있는데 저는 후자를 택했습니다.

Puzzle과 Trap은 속성이 완전히 다르다고 생각하기 때문입니다.

여기서 한가지 문제가 발생하는데, Puzzle의 범위 판정 Component가 쓸모 없어졌습니다.

범위 판정을 이미 Puzzle이 가지고 있는 Trap이 하고 있기 때문입니다.

 

이를 Interface로 개선 하면서 관계에 대해 다시 고민을 하게 되었습니다.

Puzzle은 Trap을 가지고 하나만 가지고 있을 수도 있지만, 여러개를 가지고 있을 수도 있고, 없을수도 있습니다.

부착된 Trap도 Puzzle 범위와 동일하게 작동해야 할 수도 있고, 그렇지 않을수도 있습니다.

그래서 이 구조를 어떻게 해야 할지를 고민하다가, Trap과 Puzzle의 관계부터 다시 생각을 하게 되었습니다.

 

때문에 이에 대한 답을 내는데에 시간이 좀 걸릴 것 같습니다.

우선은 남은 시간 짬짬히 Melee Attack의 판정을 다듬을 것 같습니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.07.27 개발일지  (0) 2020.07.27
20.07.26 개발일지  (0) 2020.07.26
20.07.22 개발일지  (0) 2020.07.22
20.07.20 개발일지  (0) 2020.07.20
20.07.16 개발일지  (0) 2020.07.16

https://www.acmicpc.net/problem/3300

 

3300번: 무어 기계

문제 무어 기계는 상태에 의해서 출력이 결정되는 유한 상태 기계이다. 무어 기계는 이름은 미국의 수학자이자 컴퓨터 과학자 Edward F. Moore의 이름을 따서 지었다. 무어 기계의 상태 전이는 입력�

www.acmicpc.net

버그가 나는 부분은 수정을 하였습니다.

탐색이 모두 끝날 때 되돌아 갈 위치가 없는데 받아오려 해서 문제가 생겼습니다.

 

몇가지 테스트를 하였는데 예시랑 맞지 않는 답이 출력되어 내일 자세히 디버깅을 해보려고 합니다.

원래 DirectX 공부를 하려다가 계획이 틀어져 늦게라도 개발을 하였습니다.

시간이 얼마 없어 많이 하지는 못했습니다.

 

InRange Interface를 상속 받는 HitBox를 생성해 보았습니다.

그리고 원래 계획은 각 Component마다 RPC 함수를 만들어 놓으려 했는데,

Component가 Character에 부착 될 예정이라 Character에 RPC 함수를 만들고, 
이 함수가 Interface의 함수들을 호출하도록 변경해 보았습니다.

 

여기까지 개발해보니 생각이 조금 바뀌었습니다.

원래는 각 Component가 자신이 접촉했던 Character를 관리하도록 했는데,
어차피 Character가 한 Character에게 한 모션에 1번만 타격이 가능합니다.

때문에 InRange Interface는 Character가 Implement 하도록 할 예정입니다.

 

남은 고민거리는 모션 별 판정 가능한 HitBox 지정입니다.

예를 들어 왼손 잽 공격을 할 때는 왼손의 HitBox만 타격이 가능해야 합니다.

오른발 발차기를 할 때는 오른쪽 다리의 HitBox들만 타격이 가능해야 합니다.

이를 HitBox에 Activate Interface를 부착하고 애니메이션 재생 때마다 Notify를 발생 시킬 것인지, 

충돌 함수를 따로 배정하여 각 함수별로 자신이 속한 Montage가 재생 중일 때 타격 판정을 발생시킬 것인지.

좀 더 고민을 해봐야 할 것 같습니다.

 

----------------------

 

이곳저곳 질문을 올렸는데 Notify가 생각보다 무거운 연산은 아니라고 합니다.

이 문제는 Notify 발생으로 해결을 해보고자 합니다.

'개발일지 > Treasure Hunter' 카테고리의 다른 글

20.07.26 개발일지  (0) 2020.07.26
20.07.23 개발일지  (0) 2020.07.23
20.07.20 개발일지  (0) 2020.07.20
20.07.16 개발일지  (0) 2020.07.16
20.07.15 개발일지  (0) 2020.07.15

+ Recent posts