[작성자:] 이대원

  • 디아블로3 캐릭터 정보 추출기

    디아블로3 캐릭터 정보 추출기

    디아블로3 : 영혼을 거두는 자 구매

    인터넷을 뒤지다보니 디아블로3가 망한 게임에서 탈출했음을 보았다. 2012년 제품 출시 당시, 최단 기간 가장 많이 팔린 패키지 게임이 디아블로3 였다. 그러나 블리자드가 불안정적인 서버 운영, 극악의 아이템 드롭율 설정, 사용자 계정 해킹에 대한 대응, 경매장 운영 미숙으로 즐기는 사용자가 급격하게 줄어들었다. 제작사가 2015년 확장팩 출시로 대대적인 수정을 했고, 그 결과가 망에서 갓게임로 등급 상승이다. 이런 내용과 최근 확장팩이 50% 내려갔음을 인터넷에서 보고 바로 결재를 했다. 내 스팀 계정에 찜 목록이 3개나 있는데 이를 사버리다니!!!!

    무한 파밍의 신세계지옥!!

    확장팩 구매후, 2주동안 미친듯이 파밍만 했다! 잠을 제대로 못자, 두통이 생기는 느낌이 있었다. 시차 적응이 안되는 느낌정도? 게임 개발 초기에 제작자가 사람의 심리를 잘 알아야 대박 게임을 만들 수 있다. 디아블로3의 단순화된 구성이 다음과 같다.
    1. 몹 사냥
    2. 1번으로 경헝치, 돈, 아이템 획득
    3. 특정 조건 만족시 캐릭터 능력 대폭 상승
    4. 다시 1번으로
    제작사가 컴퓨터 기술로 표현이 향상된 매우 단순한 루틴을 사용자에게 제공한다고 인식하면, 게임 디자이너가 능력자임이 확실하다.

    배틀넷 API by blizzard

    옛날부터 블리자드가 자사의 battle.net을 운영했다. 디아블로3를 시작하려면, 사용자가 이 서버에서 인증을 받아야 한다. 불안정인 서버 운영이 인증을 막았고, 사용자가 게임을 제대로 할 수 없었다. 이는 과거 환불 소동으로까지 확산되었다.
    사이트가 웹 개발자들에게 WOW, 스타크래프트, 디아블로 등 API를 제공한다. 원한다면 웹 개발자가 제공된 함수로 게임의 캐릭터가 어떤 옷을 입었는지, 몇마리의 몹을 삭제 했는지를 웹 브라우저에 표시할 수 있다. WOW가 나름 돈을 많이 버는 게임인지 관련 API가 다른 게임에 비해 상당히 많다. 그에 비해 디아블로3는 몇 개의 함수만 가지고 있다. 대표적으로 캐릭터 profile, item, 진행상태를 표시해주는 함수이다.

    사용자가 API를 사용하려면 블리자드 개발 사이트에 가입을 해야한다. 가입 후, 블리자드가 app, secert key를 주는데, 사용자가 정보를 조회하기 위해서 이 값들이 입력으로 들어가야 된다. 전에는 그렇지 않았는데, 변경되었다.

    git hub의 패키지된 코드 사용

    역시 인터넷을 뒤져보니, 어느 용자가 이미 관련 php를 모듈로 만들었다. 사용자가 이를 쓰기 위해서는 php의 모듈 관리자인 composer를 설치해야 한다.
    이 부분이 문제이다. 내 홈페이지가 워드프레스로 구성되어 있는데, composer가 이와 호환되지 않는다. 이를 해결하려면 별도 작업을 해야 하는데, 너무 멀리가는 듯 하여 그냥 쓰기로 했다.
    composer가 해당 실행되는 폴더에 composer.lock 등, 하위에 vendor를 설치한다. 위에서 예시가 되어있지만 브라우저에서 겁근 가능한 상대 경로를 지정해 놓으면 바로 사용할 수 있다.

    method 사용

    git hub에 있는 소스가 정말 사용하기 쉽다. 캐릭터를 뽑아 내려면 사용자가 battle tag를 함수의 인자로 보내줘야 한다. 이를 보내면 블리자드 서버가 캐릭터 id를 보내준다. 다시 이 id로 조회를 하면 세부적인 상태를 보내준다.
    battle tag가 영문자+#+숫자 형식이다. method가 battle tag를 인자로 가지면, #을 URI에 맞는 캐릭터로 변경해야 한다. 그러나 작성된 method의 이 부분이 살짝 미흡하다. #은 URI 코드로 보면 %23이다. 실재 dev.battle.net에서 조회되는 URI를 보면 #대신 %23으로 요청된다. 내 배틀 태그가 now0930#3468인데, “now0930%233468″로 변경해서 입력해야 한다.

    또한 제작자가 미쿡 사람인지라 미국 서버에서 관련 정보를 가져온다. git hub의 파일을 미국 서버 대신 한국 서버에서 정보를 가져오도록 kr로 바꿔야 한다. 언어를 안바꾸면 안되는데 파일 참조하여 ko_KR로 변경하면 된다. vendor/logansua/blizzard-api-client/src의 BlizzardClient.php 파일이 아래와 같다.
    59번 행 정도.

        /** 
         * Constructor
         *
         * @param string $apiKey    API key
         * @param string $apiSecret API Secret key
         * @param string $region    Region
         * @param string $locale    Locale
         */
        public function __construct($apiKey, $apiSecret, $region = 'kr', $locale = 'ko_kr')
        {   
            $options = [ 
                'apiKey'    => $apiKey,
                'apiSecret' => $apiSecret,
                'region'    => strtolower($region),
                'locale'    => strtolower($locale),
            ];
    
            $resolver = new OptionsResolver();
            $this->configureOptions($resolver, $options['region']);
    

    json 인코딩..

    php가 요청하면, 배틀넷이 관련 정보를 json 형식으로 보낸다.

    <?php
    // Include composer autoload file
     require_once __DIR__.'/../vendor/autoload.php';
    // Create a new Blizzard client with Blizzard API key and secret
    
     $client = new \BlizzardApi\BlizzardClient('unsemvbjg39hxwys5tfdaxakdedr5v95', 'JPCrKzrc7ySWJhyndx8Y9vPQQyXHMPBe');
    // Create a new Diablo service with configured Blizzard client
     $diablo = new \BlizzardApi\Service\Diablo($client);
    // Use API method for getting specific data
    //$response = $diablo->getItemDataById('Unique_Orb_Set_06_x1');
    // $response = $diablo->getHeroProfile('now0930#3468','62843148');
     //#이 URI 인코딩에 따르면 %23임..
     //#을 그대로 사용하면 error
     $response = $diablo->getCareerProfile('now0930%233468');
    //$response = $diablo->getArtisanData('blacksmith');
    // Accessing response status code
     $response->getStatusCode();
    // echo $response->getStatusCode();
    // Accessing response headers
     $response->getHeaders();
    // echo $response->getHeaders();
    // Show response body
    // echo $response->getBody();
    
    
     //아래부터는 JSON에서 데이터를 뽑아내는 부분..
    $jsonObj = json_decode($response->getBody());
    echo "<br><br>";
    echo $jsonObj->battleTag;echo "<br>";
    echo $jsonObj->heroes[1]->id;echo "<br>";
    echo $jsonObj->heroes[1]->name;echo "<br>";
    echo "Elite 몹 삭제 수 : ";print_r($jsonObj->heroes[1]->kills->elites);echo "<br>";
    //var_dump($jsonObj);
    
    $hero1 = $jsonObj->heroes[1]->id;
    echo "id is ";
    echo $hero1;
    //echo "$hero1";
    
    $response_hero = $diablo->getHeroProfile('now0930%233468',$hero1);
    $response_hero->getStatusCode();
    $response_hero->getHeaders();
    $jsonHero = json_decode($response_hero->getBody());
    //print_r($jsonHero);
    
    //echo $jsonHero;
    echo "<br><br>";
    echo "영웅 이름 :";
    print_r ($jsonHero->name);
    echo "<br>";
    for ($i=0; $i<06;$i++){
    	echo "Active Skills $i";
    	print_r ($jsonHero->skills->active[$i]->skill->name);
    	echo "<br>";
    	echo "Rune :";
    	print_r ($jsonHero->skills->active[$i]->rune->name);
    	echo "<br>";
    }
    
    //passive skills
    echo "passive skills";
    echo "<br>";
    for ($i=0;$i<3;$i++){
    	echo "$i";
    	print_r($jsonHero->skills->passive[$i]->skill->name);
    	echo "<br>";
    }
    
    
    //stats
    
    //echo $response_hero->getBody();
    
    
    
    ?>
    

    이 정보중에 무기나 마법에 대한 그림 위치가 있다. 개발자가 이를 뽑아내어 웹 브라우저에 그림으로 표시하면, 직관적인 사이트가 만들어 진다. 다만 그림들이 이름과 같이 배틀넷에서 제공되는데, 어디에서 받는지 모르겠다.
    간단히 하기위해서 나는 그냥 json을 인코딩했고, 필요 부분만 텍스트로 보여주었다. 나머지는 그냥 노가다이다.
    내 정보를 표시해 주는 주소는 다음과 같다.
    http://now0930.tk/diablo3/

  • 민수가 그린 그림

    Sorry you have no rights to view this post!

  • 인구 데이터로 년 수입 예측, 2차

    인구 데이터로 년 수입 예측, 2차

    sparse_tensor_to_dense

    전에 이어서…
    각 행의 feature를 sparse tensor로 받아 들인다. tutorial에는 값을 확인하는 부분이 없다. 내가 입력한 값이 정확한지 이를 어떻게 확인해야 하는지? print로 확인을 하면 좋은데, sparse tensor를 print 하면 대략 형식에 대한 값들만 출력이 된다.

    SparseTensor(indices=Tensor("SparseTensor_1/indices:0", shape=(32561, 2), dtype=int64), values=Tensor("SparseTensor_1/values:0", shape=(32561,), dtype=string), dense_shape=Tensor("SparseTensor_1/dense_shape:0", shape=(2,), dtype=int64))
    

    크게 3개로 구성된다. 각 항목의 type은 tensor이다.
    1. indeces에 대한 정의
    2. values에 대한 정의
    3. dense_shape에 대한 정의

    sparse_tensor_to_dense 함수가 sparse tensor를 어떻게 되어있는지 출력한다. sparse_tensor_to_dense의 입력 파라미터로 1. sparse tensor 이름, 2.default_value를 받는다. feature column으로 만든 sparse tensor는 values에 대한 data type이 string, int64 등 여러 종류가 있다. 정의된 values가 int64인데 default_value를 string을 쓰면 에러가 난다. 그래서 각 sparse tensor의 values에 대한 항목을 뽑고, 다시 dtype을 뽑아내어 각 항목에 맞는 default_value를 넣어버렸다.
    아래와 같이 구현했다.

    with tf.Session() as sess:
    
        for i in feature:
            print i
            print feature[i]
            print feature[i].values.dtype
            if(tf.string == feature[i].values.dtype):
                print "type string"
                y_tensor = tf.sparse_tensor_to_dense(sp_input = feature[i], default_value='x')
            if(tf.int64 == feature[i].values.dtype):
                print "type int64"
                y_tensor = tf.sparse_tensor_to_dense(sp_input = feature[i], default_value=0)
            else :
                print "different"
            print y_tensor
            print y_tensor.eval()
    

    이렇게 실행하면 아래의 결과를 얻는다.

    gender
    SparseTensor(indices=Tensor("SparseTensor/indices:0", shape=(32561, 2), dtype=int64), values=Tensor("SparseTensor/values:0", shape=(32561,), dtype=string), dense_shape=Tensor("SparseTensor/dense_shape:0", shape=(2,), dtype=int64))
    <dtype: 'string'>
    type string
    different
    Tensor("SparseToDense:0", shape=(32561, 1), dtype=string)
    [['Male']
     ['Male']
     ['Male']
     ..., 
     ['Female']
     ['Male']
     ['Female']]
    age
    SparseTensor(indices=Tensor("SparseTensor_3/indices:0", shape=(32561, 2), dtype=int64), values=Tensor("SparseTensor_3/values:0", shape=(32561,), dtype=int64), dense_shape=Tensor("SparseTensor_3/dense_shape:0", shape=(2,), dtype=int64))
    <dtype: 'int64'>
    type int64
    Tensor("SparseToDense_1:0", shape=(32561, 1), dtype=int64)
    [[39]
     [50]
     [38]
     ..., 
     [58]
     [22]
     [52]]
    education
    SparseTensor(indices=Tensor("SparseTensor_1/indices:0", shape=(32561, 2), dtype=int64), values=Tensor("SparseTensor_1/values:0", shape=(32561,), dtype=string), dense_shape=Tensor("SparseTensor_1/dense_shape:0", shape=(2,), dtype=int64))
    <dtype: 'string'>
    type string
    different
    Tensor("SparseToDense_2:0", shape=(32561, 1), dtype=string)
    [['Bachelors']
     ['Bachelors']
     ['HS-grad']
     ..., 
     ['HS-grad']
     ['HS-grad']
     ['HS-grad']]
    relationship
    SparseTensor(indices=Tensor("SparseTensor_2/indices:0", shape=(32561, 2), dtype=int64), values=Tensor("SparseTensor_2/values:0", shape=(32561,), dtype=string), dense_shape=Tensor("SparseTensor_2/dense_shape:0", shape=(2,), dtype=int64))
    <dtype: 'string'>
    type string
    different
    Tensor("SparseToDense_3:0", shape=(32561, 1), dtype=string)
    [['Not-in-family']
     ['Husband']
     ['Not-in-family']
     ..., 
     ['Unmarried']
     ['Own-child']
     ['Wife']]
    
  • BMW 어린이 운전교육

    BMW 어린이 운전교육

    Sorry you have no rights to view this post!

  • 인구 데이터로 년 수입 예측

    인구 데이터로 년 수입 예측

    데이터 얻기

    데이터 셋을 무료로 제공하는 사이트에서 미국 성인 나이, 학력, 가족관계, 년 소득을 받을 수 있다. 입력된 데이터로 이 사람이 연소득 50k dollar을 넘어가는지 아닌지를 예측해보려고 한다. 다행히 이 데이터 셋으로 연습한 사람들이 많은지, 조회수가 상당히 많다.

    전체적인 접근 방법

    데이터의 구조는 나이, 이름, 가족관계 등과 연 소득이 넘어가는지 아닌지로 구성되어 있다. feature로 쓸 부분을 보면 categorical, continuous 데이터로 구분된다.
    1. 파일을 training, test set으로 구분하여 읽는다.
    2. categorical 데이터는 숫자로 변경
    3. integer 데이터는 그대로 사용
    4. training..

    feature로 쓸 항목별 데이터가 상당히 많다. python의 dictionary로 일일히 쳐 변환하기는 귀찮다. 관련 소스를 찾아 보았는데 tensorflow tutorial에 이 예제가 설명을 했다.!!빙고!!!이래서 남들이 쓰는 예제를 써야한다!!

    tensorflow tutorial 정리

    튜토리얼의 구조는 대략 아래와 같다.
    1. 데이터 셋을 읽음
    2. label을 붙임(0 또는 1)
    3. feature를 categorical, continuous 기준으로 분류
    4. feature를 tensor로 읽어 들임
    5. feature를 아래 4가지 경우로 세부 분류
    – SparseColumn : categorical
    – RealValuedColumn : continuous
    – BucketizedColumn : 일정한 구간으로 분류되는 데이터들
    – CrossedColumn : 서로 연관되는 데이터들..
    6. feature에 적정한 함수를 사용하여 ID를 부여..
    7. training
    위 기능들을 모두 tensorflow가 제공하는 함수로 쉽게? 따라서 구현할 수 있다. tensorflow가 없었으면 machine learning의 접근이 상당히 어려웠다고 생각된다.

    코드를 써보고 이해하는 과정이 남아있다…

    sparse tensor, 4.15 추가분

    위 tutorial은 categorical 형식의 데이터를 sparse tensor를 사용하여 표현하였다.

    sparse_tensor = tf.SparseTensor(indices=[[0,1], [2,4]],values=[6, 0.5],dense_shape=[3, 5])

    sparse tensor는 일정 행렬에 특정 부분(indices)에만 데이터(values)가 있고 나머지 부분은 0인 텐서이다. sparse tensor 선언시 전체 행렬의 크기(dense_shape)도 선언한다. 위와 같이 만들면 [0,1]에 6이 들어가고, [2,4]에 0.5가 들어가게 된다. 전체 shape는 [3,5]로 제한된다. 대략 아래와 같은 형식의 데이터가 된다.
    [ [0 6 0 0 0 ]
    [0 0 0 0 0 ]
    [0 0 0 0 0.5 ] ]
    문제는 위의 행렬을 print로 보고 싶은데, 볼 수 있는 방법이 없다.

    sparse_tensor = tf.SparseTensor(indices=[[0,1], [2,4]],values=[6, 0.5],dense_shape=[3, 5]) 
    with tf.Session() as sess:
        print sparse_tensor
    

    이렇게 실행하면

    SparseTensor(indices=Tensor("SparseTensor_2/indices:0", shape=(2, 2), dtype=int64), values=Tensor("SparseTensor_2/values:0", shape=(2,), dtype=float32), dense_shape=Tensor("SparseTensor_2/dense_shape:0", shape=(2,), dtype=int64))

    아래와 같이 메세지가 나온다. 인터넷을 찾아 보았지만 sparse tensor를 눈으로 볼 수 있는 방법이 없어 보인다. 필요도 없어 보이고

    몇 시간의 삽질 결과..아래와 같이 결론을 내릴 수 있다.

    gender = tf.contrib.layers.sparse_column_with_keys(column_name="gender", keys=["Female", "Male"], combiner="sum")
    education = tf.contrib.layers.sparse_column_with_hash_bucket("education", hash_bucket_size=1000, combiner="sum")
    
    feature, label = train_input_fn()
    

    train_input_fn은 아래와 같다.

    def input_fn(df):
        # Creates a dictionary mapping from each continuous feature column name (k) to
        # the values of that column stored in a constant Tensor.
        # Creates a dictionary mapping from each categorical feature column name (k)
        # to the values of that column stored in a tf.SparseTensor.
        categorical_cols = {k: tf.SparseTensor(
            indices=[[i, 0] for i in range(df[k].size)],
            values=df[k].values,
            dense_shape=[df[k].size, 1])
            for k in CATEGORICAL_COLUMNS}
        # Merges the two dictionaries into one.
        feature_cols = dict(categorical_cols.items())
    
        # Converts the label column into a constant Tensor.
        label = tf.constant(df[LABEL_COLUMN].values)
        # Returns the feature columns and the label.
        return feature_cols, label
    
    def train_input_fn():
        return input_fn(df_train)
    

    1. training data에서 해당하는 column을 key 형식으로 읽어 들인다.
    2. train_input_fn에서는 해당 key별로 sparse tensor를 새로 만든다. -> 칼럼별 어느 데이터가 있는지 다른 데이터를 만든다.
    3. categorical_cols을 feature_cols로 dict 타입으로 변경한다.
    4. 위 예제에서는 key가 gender, education이 되고 values가 각 키에 해당하는 sparse tensor가 된다.

    나중에 dict 형식의 feature를 러닝하면 자동으로 변환되어서 입력이 되나보다.