perpet

 자신들이 만든 게임의 캐릭터 액션정보나 AI 스테이트 정보를 비주얼툴로 보거나 보여주고 싶을때가 있다.

간단한게 외부라이브러리를 이용해 하루만에 뷰어를 만들수있는 툴을 만들어보자.

 

작업프로세스는 이렇다.

게임 액션과 액션사이에 링크정보가 있다 이것은 특정 이벤트가 발생해야만 다른액션으로 이동을 하는 구조를 가질수있다.

가령 stand 액션에서 key_left 가 눌러지면 left_uun , key_right ,눌리면 right_run 이란 액션으로 넘어간다고 하고 이것을 텍스트 형식으로 저장을 하면

state : stand

link : key_left, left_run

link : key_right , right_run

state : left_run

state : right_run

 

이런식으로 기획자들이 자신들이 편한 방식으로 텍스트, 또는 xml 로 저장을 할것이다.

프로그래머가 자주 이 링크정보를 보고싶을때가 있는데 이정보가 일반적으로 한캐릭에 수십가지의 액션정보가 있어 텍스트로 보기로는 상당히 힘들다

 

 이런텍스트를 uml 툴같은 다이어그램으로 보고 싶을때가있는데 여기서 가장 손쉽고 빠르게 만드는 방법을 설명한다.

 

작업방식은 공개된 다이어그램 뷰어를 구해서 그포맷에 맞게 자신들의 콘텐츠정보를 컨버트 해주면된다.

여기서는 http://www.graphviz.org/ 를 사용했다. 우리가 해줄것은 이 공개소프트웨어가 인식가능한 포맷으로 나의 컨텐츠 정보를 읽어와 변경해주면된다.

 graphviz    에서 dot 포맷이 있는데 이것으로 직접 내가 포맷을 만들어줘도 되지만 이 컨버팅 작업또한 공개소스를 이용하면 간단하게 해결할수있다.

http://quickgraph.codeplex.com/ 에가면 쉽고 간단하게 dot 포맷으로 변경가능하다.

 

전체 작업방식은 내콘텐츠 정보를 읽어 QuickGraph  를 이용해 dot 포맷파일을 생성하고  graphviz 로 이파일을 가지고 원하는 다이어그램 이미지를 만들면된다.

 

 QuickGraph 로 dot 를 생성하면

 digraph G {
0 [label="stand"];
1 [label="left_run"];
2 [label="right_run"];
0 -> 1 [ label="key_left"];
0 -> 2 [ label="key_right"];
}

 

이 만들어지고

 

  graphviz  를 돌리면

 

command>  dot a.dot -Tjpg -o a.jpg

 

 

Throwing_Spirit_Grudge.gif 

 

 

 파일이 나온다.

 

 

 

 

 

 

 

 

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

사용자 삽입 이미지

소스요청하시는분들이 몇분계서서 올려요

설명은 http://blog.daum.net/hazzling/16781469
참고하시면됩니다.

1년동안 다른거 공부하랴 c# 게임서버에 신경을 못쓰고 있네요.

간단하게 제가 제시하는 코딩방식은 이렇습니다.

 

예를 들어 설명하자면 유저가 로그인하여 아이디와 패스워드를 검증하는 코딩을 짠다고 봅시다.

보통 디비쿼리시 디비에 쿼리를 날리는순간 블럭이 되여 이것을 비동기 처리합니다.

그래서 코드가 보통이렇게 되죠.

 

void login_packet(string id, string pass)

{

int x;

db.query_login(id,pass,query_resultLogin) ; // 쿼리 결과 핸들 등록

return;

}

 

void query_resultLogin(int ret)

{

   if( ret == 0)   // 결과에 따라 처리

 

   client.send_login_result(ret) ;

 

}

 

이걸 c# 에서 yield 를 이용한 코딩을 하면 간단하게

 

void login_packet(string id, string pass)

{

int x;

yield db.query_login(id,pass) ;

 

  if( ret == 0)   // 결과에 따라 처리

 

   client.send_login_result(ret) ;

}

 

이렇게 표현될수있다는겁니다.

이런구조의 장점을 들어보자면

  1.  함수하나의 스코프안에서 원하는 구현부위를 표현하여 직관적입니다.
  2. 원시적인 비동기콜방식으로 할경우 쿼리 전 변수가 쿼리결가후에도 필요하면 해당변수를 외부에 저장할 글로벌 변수로 만들어야합니다. 위에 x 라는 변수가 쿼리 결과에서도 참조가 필요한 케이스가 발생할경우..( yield 를 하면 필요가 없겠죠)
  3.  명시적인 리모트 함수선언을 할수있습니다. 지금까지는 일반적인 패킷함수를 표현하면 void login(id,pass)  를 표현하면 알아서 loing 패킷을 만들어주고 send 함수를 만들어주는 수준이지만 yield  구조를 갖으면 리턴받아야하는 패킷도 명시적으로 표현할수있습니다.  가령 int query_login(id,pass) 라고 패킷을 선언하면 컨텐츠코딩단에서 해당하는 query_login 결과값이 올때까지 다른핸들링이 작동을 안하도록 할수있습니다. 또한 요청을 수행하는 디비단이나 다른서버가 다른형식의 패킷을 보내면 시퀀스를 어겼다고 네트웍시스템이 감지할수있습니다.

     

구체적인 구조를다시 만들면

 

server 인터페이스 선언에

int login_packet(string id,string pass) ;  를 선언하고

 

int login_packet(string id, string pass)

{

int x;

yield db.query_login(id,pass) ; <= db 에 쿼리를 날리고 올때까지 알아서 기다려줌(그동안 다른 클라이언트핸들링은 처리됨)

 

  if( ret == 0)   // 결과에 따라 처리

 

yield return ret;  <= loing_packet 를 콜한 콜러에게 send 해줌 , 또한 함수 선언에 리턴값으로 int 를 준다고 했으므로 리턴이 없거나 다른형을날리면 네트웍 시스템이 인지하여 경고처리가 가능

}

 로 표현이 됩니다.  마치 일반 어플리케이션 코딩하듯이 하면된다는거죠

 

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

on intelligence 란 책을 읽었습니다.

http://www.yes24.com/24/goods/3794199

번역본이 나와 편하게 읽을수있었다.
인간의 인식에대한 하나의 이론을 설명하는데
매우 흥미가 있었다.
개인적으로 인공지능에 관심이 많아 책을보면서 저자가 설명하는 구체적인 인식방식에 대해 프로그래밍해보고
싶어 구글링을 통해 관련 코드를 찾아봤다.


http://en.wikipedia.org/wiki/On_Intelligence

마지막에 http://sourceforge.net/projects/neocortex/
링크가 걸려 분석을 해봤다.

대충 분석을 해봤지만 특정 문제해결에 한가지 방법이 될분 딱히 새로운 솔루션이라는 느낌이 오지 않았다.


다음은 소스 빌드 방식이다. 소스는 vs2010 버전에서 빌드가 되도록 수정해서 https://captig.googlecode.com/svn/trunk/Neocortex 에 올렸다 다음은 빌드에 필요한 몇가지 설명을 한다. 1.원본소스는 qt-toolkit 을 사용한다 gui 를 표현하기 위해 이 툴킷을 사용했다. 그러므로 http://qt.nokia.com/downloads 사이트에 들어가 윈도우용 버전을 다운받는다..
빌드 구성당시 내가 사용한 버전은 qt-win-opensource-4.6.3-vs2008.exe 이다..
2. 환경변수에 "QTDIR" = "C:\Qt\4.6.3\" 을 넣는다. 디폴트로 깔경우의 경로다..
2.https://captig.googlecode.com/svn/trunk/Neocortex 에 들어가서 소스를 다운받는다..
3.captig\Neocortex\Neocortex Minimal Source Code\Neo1.4.2c Source\MS Studio 20051.4.2c\Neo.sln 을 열어 neo 프로젝트를 실행하면 된다..
4.실행을 하면 관련 dll 이 없다고 한다. c:\qt\4.6.3\bin 에서 QtCore4.dll,QtCored4.dll,QtGui4.dll,QtGuid4.dll 를 실행폴더에 복사한다 디폴트이면 debug 폴더에 넣으면된다..
관련 분석 문서는 https://docs.google.com/document/edit?id=1sRWtPy_cfZ-WWn6ucq5Tr_THwQS2u5AOuT8UPnUPLMA&hl=en 참고

http://robot114.com/cafe/club/club_show.asp?c_idx=11

자주가던 로봇커뮤니티게시판에 로봇대회 홍보글이 올라와서 대회에 참가하게 됬습니다.
예전에 하던 msrds 개발 방법과 비슷하고 단지 c++ 로 프로그램을 짜면 되는 수준이였죠
주로 혼자 대회를 참가하는걸 좋아하는데 대회 룰상 그룹으로만 참여가 가능하더군요.
그래서 회사 직원들을 꼬셨습니다.
처음에는 멘토를 하던 직원들을 꼬셨지만 그들만 믿고 진행하기는 힘들어
주위 여러명을 우선 꼬셨습니다. 그중에 지쳐 떨어져나가도 프로젝트가 진행될수있는 인원으로요.
그리고 마지막에 나를 포함 4명이 남고 프로젝트를 진행했죠.
대회룰상 3명이상만 되기때문에 팀이 진행하는데는 문제가 없었습니다.
하지만 한팀보다는 여러팀으로 나가는게 좋겠다 싶어 2명을 더꼬셔 2팀 3명씩 본선대회에 출전했습니다.
팀원을 나눌때 상금 문제가 걸리더군요. 누가 더열심히 하고 누가 덜한 문제가 발생하여 형평성 문제로
고민하다 상금은 무조건 한팀만 타도 6명이 나누기로 했습니다.

대회 진행을 제가 시작했기때문에 이결정을 제가 했는데.. 결론적으로 무지 잘한판단이 였습니다.
결론적으로 저희팀은 본선 16강에서 실수로 테스트코드가 들어가서 바로 탈락을 하고 저희 다른팀은
훌륭하게 1등을 했거든요..ㅋㅋㅋㅋ 아쉽게 이력서에 올릴건 없지만 상금은 서로 나눠먹었네요...

그나저나 대회를 준비하면서
회사에서 같이 일하는 거나 별반 다른게 없는것 같습니다. 서로 목적의식?이 다르다보니
얼만큼 그들 각자에게 이대회가 어떠한 만족감이 있는지 인식시켜주며 진행하는것은 회사에서 프로젝트 진행하는거랑 같은 이슈?상황이라 보여집니다.

하여간 대회상금으로 회식도 하고 돈도 챙겨.. 나름 고생한 보람이 있었네요.


지금은 딱히 나갈대회가 없어 개인적으로 하고싶던 개인프로젝트를 위해 인공지능을 공부하고 있네요.

사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지

예전에 조립정도만 하고 말았던 레고로봇
이제야 비젼도 넣어보고 해서 로봇을 만들어 컨트롤 해보는 것을 해본다.

 

 

 

 

 

 

 

 

 작업하다보면 foreach 안에서 remove 를 할때가 꼭있다.

속도무시하고 편하게 사용하고 싶을때 사용하면 좋을듯하다.

어떤식으로 구현하는지 의도만 파악하고 자신의 입맛대로 바꾸길 바란다.

 

lib 코드

  1.  
  2. using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
  3. /// 버전 0.1
    /// perpet@hitel.net 구본수
    /// 소스 수정해서 사용하는건 문제 없으나 수정을 하면 반드시 원 제작자에게 수정된 소스를 메일로 보내야합니다. 버그나 개선된것을 꼭알려달라는 뜻입니다.
    ///
    /// 초기 버전이라 버그가 많을수있습니다..
  4.  
  5. namespace Foreach_safe_list
    {
  6.     class SafeCollection<TKey, TValue> //: Dictionary<TKey, TValue>
        {
            //원본리스트
            Dictionary<TKey, TValue> _List = new Dictionary<TKey,TValue>();
  7.         // 임시 add list
            Dictionary<TKey, TValue> _AddList = new Dictionary<TKey, TValue>();
  8.         // 임시 remove list
            List<TKey> _RemoveList = new List<TKey>();
  9.         public Dictionary<TKey, TValue>.KeyCollection Keys
            {
                get { return _List.Keys; }
           
  10.        }
  11.  
  12.         public void Clear()
            {
                if (bForeachCount == 0)
                {
                    _List.Clear();
                }
                else
                {
  13.                 _AddList.Clear();
                    _RemoveList.Clear();
  14.                 foreach(TKey key in _List.Keys)
                        _RemoveList.Add(key);
  15.             }
  16.         }
  17.        public Dictionary<TKey, TValue>.ValueCollection Values {
               get{return _List.Values;}
            }

  18.         public bool ContainsKey(TKey key)
            {
                // 제일 먼저 add list 에서 찾아봐야한다
                bool ret = _AddList.ContainsKey(key);
                if (ret == true)
                    return true;
                // 그다음 remove list 에서 찾아봐야한다.
                if (_RemoveList.Contains(key))
                {
                    return false;
                }
  19.             return _List.ContainsKey(key);
            }
  20.         public bool TryGetValue(TKey key, out TValue value)
            {
                if (bForeachCount == 0)
                {
                    return _List.TryGetValue(key, out value);
                }
                else
                {
                    // 제일 먼저 add list 에서 찾아봐야한다
  21.                 bool ret = _AddList.TryGetValue(key, out value);
                    if (ret == true)
                        return true;
  22.                 // 그다음 remove list 에서 찾아봐야한다.
                    if (_RemoveList.Contains(key))
                    {
                        return false;
                    }

  23.                 return _List.TryGetValue(key, out value);
                }
  24.             //return false;
            }

  25.   public void Add(TKey key ,TValue value)
      {
                if (bForeachCount == 0)
                {
                    _List.Add(key, value);
                }
                else
                {
                    _AddList.Add(key,value);
                }
  26.   }

  27.         public void Remove(TKey key)
            {
                if (bForeachCount == 0)
                {
                    _List.Remove(key);
                }
                else
                {
                    if (_AddList.ContainsKey(key))
                    {
                        _AddList.Remove(key);
                    }
                    else
                    {
                        if (!_List.ContainsKey(key) || _RemoveList.Contains(key))
                        {
                            throw new Exception("dosen't have key");
                        }
  28.                     _RemoveList.Add(key);
                    }
                   
                }
            }
           
            public void StartForeach()
            {
                bForeachCount++;
            }
  29.         public void EndForeach()
            {
                bForeachCount--;
                if (bForeachCount == 0)
                {
  30.                 // remove 먼저해야함
  31.                 foreach (TKey item in _RemoveList)
                    {
                        _List.Remove(item);
                    }
  32.                 _RemoveList.Clear();
  33.                 foreach (KeyValuePair<TKey, TValue> item in _AddList)
                    {
                        _List.Add(item.Key, item.Value);
                    }
                    _AddList.Clear();
                }
            }

  34.   public uint bForeachCount = 0;
  35.         public Dictionary<TKey, TValue>.Enumerator BaseGetEnumerator()
      {
                return _List.GetEnumerator();
      }
  36.   public IEnumerator GetEnumerator()
      {
       return new SafeEnumerator(this);
      }

  37.         class SafeEnumerator : IEnumerator, IDisposable
            {
                SafeCollection<TKey, TValue> _list;
                Dictionary<TKey, TValue>.Enumerator enumerator;
  38.             public object Current { get { return enumerator.Current; } }
                public bool MoveNext() {
  39.                 while (enumerator.MoveNext())
                    {
                        if (_list._RemoveList.Contains(enumerator.Current.Key))
                            continue;
                        return true;
                    }
  40.                 return false;
               
                }
                public void Reset() {
                    //enumerator...Reset();
                }
  41.             public SafeEnumerator(SafeCollection<TKey, TValue> list)
                {
                    _list = list;
                    enumerator = _list.BaseGetEnumerator();
  42.                 _list.StartForeach();
  43.             }
  44.             public void Dispose()
                {
                    _list.EndForeach();
                }
            }

  45.  }
    }

 

test code

 

  1.  sdsd
  2. using System;
    using System.Collections.Generic;
    using System.Text;
  3. namespace Foreach_safe_list
    {
  4.     class buff
        {
            public bool _flag;
            public buff(bool flag)
            {
                _flag = flag;
            }
          
            public void update(){}
          
           
        }
  5.  class Program
     {
  6.  
  7.   static void Main(string[] args)
      {
                SafeCollection<int, buff> a = new SafeCollection<int, buff>();
  8.             a.Add(1,new buff(true));
                a.Add(2,new buff(true));
                a.Add(3,new buff(false));
                a.Add(4,new buff(true));

  9.             foreach (KeyValuePair<int, buff> aaa in a)
       {
                    if( a.ContainsKey(2))
                        a.Remove(2);
  10.                 foreach (KeyValuePair<int, buff> bbb in a)
                    {
                        bbb.Value.update();
                        if (bbb.Value._flag == false)
                            a.Remove(bbb.Key);
                    }
                }
  11.             foreach (KeyValuePair<int, buff> aaa in a)
                {
                    if (a.ContainsKey(2))
                        a.Remove(2);
  12.                 foreach (KeyValuePair<int, buff> bbb in a)
                    {
                        if (!a.ContainsKey(2))
                            a.Add(2, new buff(true));
  13.                 }
  14.             }
  15.             foreach (KeyValuePair<int, buff> aaa in a)
                {
                    Console.WriteLine("{0}", aaa.ToString());
                }
  16.  
  17.   }
     }
    }
  18.  

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

튜링봇

개인 프로젝트2009. 9. 27. 06:09
튜링테스트 라고 들어보셨을겁니다. 컴퓨터로 채팅을 하는데 채팅하는대상이 사람인지 봇인지 대화를 통해 알아맞추는것이죠. 저는 이걸 확장해서 게임상에 만난 캐릭과 같이 미션을 수행하는데 그 캐릭이 사람인지 봇인지 판단할수 없도록 하는 시스템을 만들고 싶었습니다. 앞으로 이걸 "튜링봇" "튜링시스템" 이라 할 생각입니다.


게임에서의 튜링봇을 의미하기위해
튜링 NPC 가 어떨까 하기도 하고.

2.퀘스트

스터디/C++,CLI,C#2009. 9. 14. 20:11

 

그렇다면 이것으로 간단한 퀘스트를 짜보고 간단한 핵심 코드 리뷰를 해보자

Demo2

일반적으로 기획자가 상상하는 단순한 퀘스트를 상상해보자.

1.유저가 npc 를 클릭한다.

2.유저에게 A 를 만나달라고 대화박스를 보인다

3.승낙을 하면 아이템을 준다.

4.A를 클릭하면 고맙다고 한다.

아주일반적인 퀘스트 내용이다.

 

이것을 정말 저런순서대로 코딩을 할수없을까?

 

밑에 코드를 보자

 

quest1 버튼을 누르면 밑에 코드가 돌아간다.

 

우선 퀘스트 코드를 보자

 [소스1]


  1.         public IEnumerator GetEnumerator()
            {

    // 이미 퀘스트를 받았나?

                if (_Owner._QuestInfo.Contains(1))
                {

  2.        

            yield return new NCondition.WaitMessage("여기서 뭐하나", 2);

                    yield break;
                }

  3.            // 부탁을 한다.

                NCondition.WaitMessage msg = new NCondition.WaitMessage("반갑군. 내부탁을 들어줄수있나?", 0);

                yield return msg;

  4.            
  5.             if (msg.Ret() == "OK")
                {

                // ok 이면 퀘스트를 준다

                    _Owner._QuestInfo.Add(1);

                    yield return new NCondition.WaitMessage("고맙군 어서 친구를 찾아주게 ", 2);
                }
                else
                {

  6.                //

        yield return new NCondition.WaitMessage("실망일세 꺼지라우", 2);

                    yield break;
                }

  7.             // A를 클릭하면

                yield return new NCondition.WaitClickEvent(1);

  8.          
  9.             // 보상을 해주던지 다른이벤트를 하던지 하여간 여기서 끝냄

                yield return new NCondition.WaitMessage("반갑군 자네가 그친구인가? 너무늦었네... ", 5);


                yield break;
            }

     

     


 

한마디로 yield 로 표현된 코드는 우리가 일일히 스테이트관리를 해야하는작업을 코드가 알아서 만들어준다고 보면된다.

우리는 귀찮은 스테이트 관리와 데이타 관리를 컴파일러에게 넘겨주고 우리가 필요한 컨텐츠 코드작성만 하면되는것이다.

 [소스2]

  1.  
            public void Update()
            {
  2.             // 등록된 enumerable 오브젝트를 가지고 있다.
                // for 문중 ProcessList 에 있는 아이템을 지울수있는 구조를 위에 뒤에서 앞으로 for 문을 돈다.(성능은 좋지않다)
               
                for (int index = ProcessList.Count - 1; index >= 0; index--)
                {
                    JobInfo pProcess = ProcessList[index];
  3.                 // 현재 스크립트용 함수에서 yield return new icondition 류가 있는지 본다.
                    if (pProcess.m_Condition != null)
                    {
                        // yield return new icondition 이 있으면 완료가 될때까지 스킵한다.
                        if (pProcess.m_Condition.IsComplete() == false)
                        {// 선행작업이 완료되지 않았다.
                            continue;
                        }
                    }
  4.                 // yield 나 yield return 이 올때까지 프로세스가 진행된다.
                    if (pProcess.m_ProcessEnum.MoveNext() == true)
                    {
                        // 유저가 요정한 yield return new icondition 을 저장한다.
  5.                     pProcess.m_Condition = (ICondition)pProcess.m_ProcessEnum.Current;
                        continue;
                    }
                    else
                    {
                        // enumerable 문을 다 완료하였다면 지운다.
                        ProcessList.Remove(pProcess);
                    }
                }
  6.         }

위[소스2] 코드는 [소스1] 의 코드를 어떻게 제어할수있는지 보여주는 코드이다.

이샘풀소스에 위코드 두가지를 이해한다면 핵심적인 내용은  다이해한거라 볼수있다.


다음에는 이 yield 프로그래밍을 가지고 분산프로그램을 쉡게 구현할수있는 방법을 소개해 보겠다.

이 글은 스프링노트에서 작성되었습니다.