post list

2015년 8월 18일

[Tizen] EFL을 위한 Vim 환경 설정


EDC Support 파일 다운로드를 다운로드 하자. 아래의 링크에서 Zip을 다운로드 받는다

https://git.enlightenment.org/editors/vim-configs.git/tree/syntax

다운로드 된 파일은 압축을 풀어서 ~/vim/bundle/ 에 넣어 놓는다.

끝!



# 참고 : https://github.com/vim-scripts/edc-support

# 참고 : http://storycompiler.tistory.com/57


































[Tizen] EFL Relative



description {
..
rel1 {
relative: 0.0 0.0;
offset: 0 0;
to: [another part's name]
}
..
rel2 {
relative: 1.0 1.0;
offset: -1 -1;
to: [another part's name]
}
..
}
The rel1 and rel2 blocks are used to define the position of each corner of the part's container. With rel1 being the left-up corner and rel2 being the right-down corner.



EFL에서 좌표는 왼쪽 상단을 기준(Origin)으로 한다. 즉, (0,0)은 왼쪽 상단을 의미하며 (1,1)은 오른쪽 하단을 의미한다.

relative 속성에서 rel1 은 왼쪽 위의 좌표를 의미하며, rel2는 오른쪽 아래의 좌표를 의미한다. 이 두 좌표로 하나의 Part의 위치를 지정한다. 즉, description이라는 상자의 위치를 정하는데 사용 하는 block 이다.


relative (in rel1 or rel2) : to 속성에 지정된 Part를 기준으로 상대적인 좌표를 지정하는 속성이다. relative [x axis] [y axis] 로 두개의 인자를 가진다. x axis가 0일 경우 to에 지정된 part의 왼쪽 x좌표를 가져오며 1일 경우 맨 오른쪽 x좌표를 가져온다. y axis도 마찬가지다.



relative [X axis] [Y axis]
Moves a corner to a relative position inside the container of the relative "to" part. Values from 0.0 (0%, beginning) to 1.0 (100%, end) of each axis.



offset (in rel1 or rel2) : to 에 지정된 Part와의 절대적인 거리를 둔다




offset [X axis] [Y axis]
Affects the corner position a fixed number of pixels along each axis.



to (in rel1 or rel2) : 어떤 대상을 기준으로 삼을 것인지 지정하는 속성이다. part name을 적어주면 된다.



to [another part's name]
Causes a corner to be positioned relatively to another part's container. Setting to "" will un-set this value for inherited parts.



to_x (in rel1 or rel2) : x 좌표만 기준으로 삼을 part의 name을 지정하는 속성이다. relative 에서 [x axis] 에 지정된 값이 to_x로 지정된 part의 x값을 결정하게 된다.



to_x [another part's name]
Causes a corner to be positioned relatively to the X axis of another part's container. Simply put affects the first parameter of "relative". Setting to "" will un-set this value for inherited parts.


to_y (in rel1 or rel2) : to_x와 마찬가지다. to_x 와 to_y가 겹치는 곳 사이가 결국 기준점이 된다.


to_y [another part's name]
Causes a corner to be positioned relatively to the Y axis of another part's container. Simply put, affects the second parameter of "relative". Setting to "" will un-set this value for inherited parts.
































2015년 8월 17일

[Tizen] EFL Text


part {

description {
..
text {
text: "some string of text to display";
font: "font_name";
size: SIZE;
text_class: "class_name";
fit: horizontal vertical;
min: horizontal vertical;
max: horizontal vertical;
align: X-axis Y-axis;
source: "part_name";
text_source: "text_part_name";
ellipsis: -1.0 (since 1.8), 0.0-1.0;
style: "stylename";
}
..
}
}

 

min (in text block) : 만약 0이 아닌 어떤 값(보통 1)이 horizontal 이나 vertical 위치에 들어가게 되면 아무리 크기를 얼마로 잡아놨든 글자에 맞게 최소 크기를 확장된다. 본래 크기가 크다면 상관 없지만 글자영역보다 작다면 확장된다는 의미다.

max [horizontal] [vertical]
 When any of the parameters is enabled (1) it forces the maximum size of the container to be equal to the maximum size of the text. The default value is "0 0".




max (in text block) : 만약 0이 아닌 어떤 값(보통 1)이 horizontal 이나 vertical 위치에 들어가게 되면 아무리 크기를 얼마로 잡아놨든 글자에 맞게 최대 크기가 확장된다. max 와 min의 차이가 뭘까.


max [horizontal] [vertical]
 When any of the parameters is enabled (1) it forces the maximum size of the container to be equal to the maximum size of the text. The default value is "0 0".














ellipsis (in text block) : 글자 수가 많아서 본래 크기를 넘어버릴 때 생략 (...)을 한다. 0.0이면 글자의 앞을 살리고(끝을 생략한다는 의미) 1.0이면 글자의 뒤를 살린다(앞을 생략한다). 0.5라면? 중간을 살리고 양쪽을 생략해버린다.

ellipsis [point of balance]

Used to balance the text in a relative point from 0.0 to 1.0, this point is the last section of the string to be cut out in case of a resize that is smaller than the text itself. The default value is 0.0.



아래는 EFL 문서에 있는 본문이다.
(https://docs.enlightenment.org/auto/edje/edcref.html)


2015년 8월 13일

[Cocos2d-x] 화면 사이즈

http://egloos.zum.com/icegeo/v/298307

[Android] Service

Service는 Thread와 마찬가지로 백그라운드 작업자다. 그러나 가장 큰 차이점은 명시적으로 Kill 하지 않으면 죽지 않는다는 점인데, 명시적 Kill 하지 않았는데 죽을 경우에는 다시 살아난다.

1. Unbound Service (or Started Service)
startService(); 로 호출

이러한 서비스는 작업 결과를 돌려주기 위해서는 BroadcastReceiver를 만들어서 결과를 받아야 한다.

그 Receiver는 Activity 와 상호 작용하여 UI를 업데이트 해야 한다.


2. Bound Service
bindService(); 로 호출

Unbound Service와 달리 Activity와 즉각 상호작용이 가능하다.



p.s 간단하게 썼지만 BroadcastReceiver 와 Activity간의 상호작용은 꽤나 어렵다. 다음의 예제에서 답을 찾을 수 있다.


package com.example.usecalcproject;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.example.calcproject.ICalc;

public class MainActivity extends Activity implements View.OnClickListener {

private EditText mEditExpr1;
private Button mBtnCalc1;
private TextView mtxtResult1;

private EditText mEditExpr2;
private Button mBtnCalc2;
private TextView mtxtResult2;

private BroadcastReceiver mBR;

/* 옵션 구현(1/5) */
ICalc mCalc;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mEditExpr1 = (EditText) findViewById(R.id.editExpr1);
mBtnCalc1 = (Button) findViewById(R.id.btnCalc1);
mBtnCalc1.setOnClickListener(this);
mtxtResult1 = (TextView) findViewById(R.id.txtResult1);

mEditExpr2 = (EditText) findViewById(R.id.editExpr2);
mBtnCalc2 = (Button) findViewById(R.id.btnCalc2);
mBtnCalc2.setOnClickListener(this);
mtxtResult2 = (TextView) findViewById(R.id.txtResult2);

if (savedInstanceState != null) {
String expr1 = savedInstanceState.getString("expr1", "");
mEditExpr1.setText(expr1);
String result1 = savedInstanceState.getString("result1", "");
mtxtResult1.setText(result1);
String expr2 = savedInstanceState.getString("expr2", "");
mEditExpr2.setText(expr2);
String result2 = savedInstanceState.getString("result2", "");
mtxtResult2.setText(result2);
}

mBR = new MyReceiver();
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("expr1", mEditExpr1.getText().toString());
outState.putString("result1", mtxtResult1.getText().toString());
outState.putString("expr2", mEditExpr2.getText().toString());
outState.putString("result2", mtxtResult2.getText().toString());
}

// Inner class
// Receiver가 Activity에 접근하는 하나의 방법
private class MyReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
String result = intent.getStringExtra("result");
mtxtResult1.setText(result);
}
}

@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter("example.mytest.CALCRESULT");
registerReceiver(mBR, filter);

/* 옵션 구현(2/5) */
Intent i = new Intent();
i.setClassName("com.example.calcproject", "com.example.calcproject.CalcService");
bindService(i, srvConn, BIND_AUTO_CREATE);
}

@Override
protected void onPause() {
super.onPause();
unregisterReceiver(mBR);

/* 옵션 구현(3/5) */
unbindService(srvConn);
}

/* 옵션 구현(4/5) */
ServiceConnection srvConn = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName className, IBinder binder) {
mCalc = ICalc.Stub.asInterface(binder);
}

@Override
public void onServiceDisconnected(ComponentName className) {
mCalc = null;
}
};

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnCalc1:
if (mEditExpr1.getText().length() > 0) {
Intent i = new Intent();
i.setClassName("com.example.calcproject", "com.example.calcproject.CalcService");
i.putExtra("expr", mEditExpr1.getText().toString());
startService(i);
}
break;
case R.id.btnCalc2: /* 옵션 구현(5/5) */
if (mEditExpr2.getText().length() > 0) {
try {
String result = mCalc.calc(mEditExpr2.getText().toString());
mtxtResult2.setText(result);
}
catch (RemoteException e) {
e.printStackTrace(); // Security risk!
}
}
break;
}
}
}



해결 방법 중 하나인 AIDL 파일
package com.example.calcproject;

interface ICalc {
String calc(String expr);
}


[Android] Thread

DDMS 에서 원하는 앱을 선택하고 왼쪽고 상단에 화살표 세개 모양의 아이콘을 눌리고 오른쪽 창에서 같은 아이콘 모양의 Threads를 눌리면 현재 앱의 Thread 상태들을 볼 수 있다.

Android는 다음의 방식으로 Thread를 지원한다.

1. Looper
- 메시지 큐를 생성하고 초기화
- 메시지 큐에서 데이터를 꺼내서 Handler에게 전달

2. Handler
- Looper가 전달해 준 데이터를 처리
- 메시지 큐에 데이터를 삽입


UI Main Thread 도 Looper를 가지고 있으나 우리의 코드에 보이지는 않는다. 다만 Handler를 붙여서 Looper를 이용할 수는 있다.

보통 UI Thread와 다른 Thread (이하 Worker Thread) 간의 통신을 위해서 Handler를 사용하게 되는데 양방향 통신을 위해서는 Handler 또한 양 쪽 Thread 모두에게 붙여야 한다. Handler는 UI Thread와 Worker Thread에서 모두 접근 가능하도록 코드를 작성 해야 한다.

예제는 다음과 같다.

public class C16_Handler extends Activity {

int mMainValue = 0;
int mBackValue = 0;
TextView mMainText;
TextView mBackText;

@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.c16_thread);

mMainText = (TextView) findViewById(R.id.mainvalue);
mBackText = (TextView) findViewById(R.id.backvalue);

BackThread thread = new BackThread();
thread.setDaemon(true);
thread.start();
}

public void mOnClick(View v) {
mMainValue++;
mMainText.setText("MainValue : " + mMainValue);
}

class BackThread extends Thread {

@Override public void run() {
while (true) {
mBackValue++;
// mHandler.sendEmptyMessage(0);
Message msg = new Message();
msg.what = 0;
msg.arg1 = 0;
msg.arg2 = 0;
msg.obj = null;
mHandler.sendMessage(msg);

try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
;
}
}
}
}

Handler mHandler = new Handler() {

@Override public void handleMessage(Message msg) {
if (msg.what == 0) {
mBackText.setText("BackValue : " + mBackValue);
}
}
};
}


Message를 다루는 방법도 여러가지가 있는데 여기서는 다루지 않겠다. 다만 다음과 같이 Pool 방식도 있음을 알아두자.

Message msg = Message.obtain();


다음은 명시적으로 Looper를 이용하는 방식의 예제다.


public class C16_Looper extends Activity {

int mMainValue = 0;

TextView mMainText;
TextView mBackText;
EditText mNumEdit;

CalcThread mThread;

@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.c16_looper);

mMainText = (TextView) findViewById(R.id.mainvalue);
mBackText = (TextView) findViewById(R.id.backvalue);
mNumEdit = (EditText) findViewById(R.id.number);

mThread = new CalcThread(mHandler);
mThread.setDaemon(true);
mThread.start();
}

public void mOnClick(View v) {
Message msg;

switch (v.getId()) {
case R.id.increase:
mMainValue++;
mMainText.setText("MainValue : " + mMainValue);
break;
case R.id.square:
msg = new Message();
msg.what = 0;
msg.arg1 = Integer.parseInt(mNumEdit.getText().toString());
mThread.mBackHandler.sendMessage(msg);
break;
case R.id.root:
msg = new Message();
msg.what = 1;
msg.arg1 = Integer.parseInt(mNumEdit.getText().toString());
mThread.mBackHandler.sendMessage(msg);
break;
}
}

Handler mHandler = new Handler() {

@Override public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
mBackText.setText("Square Result : " + msg.arg1);
break;
case 1:
mBackText.setText("Root Result : " + ((Double) msg.obj).doubleValue());
break;
}
}
};
}

class CalcThread extends Thread {

Handler mMainHandler;
Handler mBackHandler;

CalcThread(Handler handler) {
mMainHandler = handler;
}

@Override public void run() {
Looper.prepare();

mBackHandler = new Handler() {

@Override public void handleMessage(Message msg) {
Message retmsg = new Message();
switch (msg.what) {
case 0:
try {
Thread.sleep(200);
}
catch (InterruptedException e) {
;
}
retmsg.what = 0;
retmsg.arg1 = msg.arg1 * msg.arg1;
break;
case 1:
try {
Thread.sleep(200);
}
catch (InterruptedException e) {
;
}
retmsg.what = 1;
retmsg.obj = new Double(Math.sqrt(msg.arg1));
break;
}
mMainHandler.sendMessage(retmsg);
}
};

Looper.loop();
}
}






[Android] SaveInstanceState

안드로이드는 화면 회전이 일어나면 onCreate가 다시 호출된다. 게다가 변수들의 값까지 초기화 되어 버린다. 이럴 때 사용하는 것이 onSaveInstanceState 함수이다.

아래와 같이 쓸 수 있다.

private int num = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if(savedInstanceState != null){
//화면 회전 등으로 인한 일시적인 파괴 후 재생성
num = savedInstanceState.getInt("num", 0);
}

findViewById(R.id.button1).setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("num", num);
}

[Android] Preference

Context.getSharedPreference() : 응용프로그램 간에 서로 공유할 수 있다.
저장되는 데이터는 다음의 경로에 저장된다.

/data/data/com.your.package/shared_prefs/com.your.package_preferences.xml

반면 Activity.getPreference() 는 해당 어플리케이션만 사용할 수 있다.



예제 코드

public class MainActivity extends Activity {

private EditText edit;
private Button btn;

@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

edit = (EditText) findViewById(R.id.editText1);
edit.setOnKeyListener(new View.OnKeyListener() {

@Override public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
Toast.makeText(MainActivity.this, edit.getText().toString(), Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});

btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener(new View.OnClickListener() {

@Override public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, ModifyText.class);
startActivity(intent);
}
});
}

@Override protected void onPause() {
super.onPause();
SharedPreferences setting = getSharedPreferences("mysetting", MODE_PRIVATE);
// SharedPreferences setting = getPreferences(MODE_PRIVATE);
Editor editor = setting.edit();
editor.putString("mytext", edit.getText().toString());
editor.commit();
}

@Override protected void onResume() {
super.onResume();
SharedPreferences setting = getSharedPreferences("mysetting", MODE_PRIVATE);
// SharedPreferences setting = getPreferences(MODE_PRIVATE);
edit.setText(setting.getString("mytext", ""));
}
}

[Android] Adapter

Grid View 로 예를 들어보자. 우선 XML 파일은 다음과 같다.

<GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:columnWidth="90dp"
    android:gravity="center"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="10dp" />

android:horizontalSpacing 은 아이템 간의 간격
android:numColumns 는 컬럼의 갯수를 알아서 처리함을 의미
android:stretchMode 는 남는 공간을 위에서 지정한 90dp 에 더해서 아이템을 늘린다.


class 파일은 다음과 같다.

GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));

gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(v.getContext(), position + " selected!", Toast.LENGTH_SHORT).show();
}
});

보통 위와 같은 3가지 로직을 거친다. 뷰를 가져오고, 어댑터를 붙이고, 리스너를 붙인다.

뷰를 가져오는 것은 쉬우니 생략한다. 어댑터를 구현하는 방법을 알아보자. 안드로이드에서 제공하는 기본 어댑터보다는 우리가 만들어보자.

public class ImageAdapter extends BaseAdapter {

private Context mContext;

public ImageAdapter(Context c) {
mContext = c;
}

public int getCount() {
return mThumbIds.length;
}

public Object getItem(int position) {
//return "position " + position + " data";
return null;
}

public long getItemId(int position) {
return position;
}

// Create a new ImageView for each item referenced by the Adapter.
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = (ImageView) convertView;

if (imageView == null) { // If it's not recycled, initialize some attributes.
LayoutInflater inflater = LayoutInflater.from(mContext);
imageView = (ImageView) inflater.inflate(R.layout.my_list_item, parent, false);
}

imageView.setImageResource(mThumbIds[position]);
return imageView;
}
}

핵심적인 코드는 getView 함수다. 그 중에 convertView는 재활용 가능한 뷰로서 null 일때만 만들어주면된다. 리턴되는 view는 그리드 뷰에서 하나의 공간을 차지하는 View를 의미한다. 여기서는 단순 이미지를 리턴한다. 좀 더 복잡한 View를 만들고 싶다면 my_list_item.xml 을 수정하면 된다. 지금은 ImageView만을 리턴하기 때문에 my_list.item 은 다음과 같이 구현되어 있다.

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/imgItem"
    android:layout_width="85dp"
    android:layout_height="85dp"
    android:padding="5dp"
    android:scaleType="centerCrop" >
</ImageView>


2015년 8월 12일

[Cocos2d-x] Android 개발 환경 구축

우선 기본적인 Cocos2d-x 개발 환경이 구축되었다는 가정하에 이 포스팅을 쓴다. 기본적인 Cocos2d-x 개발 환경 구축이 되어 있지 않다면 아래의 포스트를 따라한 뒤에 다시 이 곳으로 돌아오길 바란다.

http://nospblog.blogspot.kr/2015/05/tizen.html

cocos2d-x, python 의 환경 변수 설정이 되어 있고 새로운 프로젝트까지 무사히 완성했다면 Android 에서 포팅할 수 있고 개발도 할 수 있는 환경을 마련해보자. (참고로 JDK설치는기본이다? 당연히 설치해야한다. 이것도 모른다면 다른 곳에서 설치하는 방법을 알아보고 오라!)


1. Eclipse 설치
Eclipse 를 설치한다. 아래의 링크에서 CDT가 아닌 Java EE 버전을 설치한다.

https://eclipse.org/downloads/


2. CDT Plugin 설치
Eclipse에서 Eclipse Marketplace에 접속해서 CDT 라고 검색하면 Eclipse CDT가 검색되며 그것을 선택해서 설치하면 된다.


3. NDK 설치
아래의 링크에서 Download 항목에서 NDK를 다운로드 받는다.

https://developer.android.com/ndk/downloads/index.html

다운로드 파일을 실행시키면 꽤나 오랫동안 압축을 풀게 된다. 압축을 풀게 되면 NDK 폴더가 생성이 될 것인데 자신이 원하는 곳으로 옮기도록 하자.  /Document 폴더에 두기로 한다면... 다음과 같은 경로가 나올 것이다. 잘 기억해 둔다.

C:\Users\deokil\Documents\android-ndk-r10e\


4. ADT 설치
사실 이제는 Android Studio 로 합쳐져 버린 녀석이다. 하지만 우리는 Cocos2d-x를 위해서 따로 설치를 해야한다. Eclipse에 접속한 뒤에 Help > Install new software를 선택한다. 그리고 Work with 항목에 다음의 링크를 입력해 준다.

https://dl-ssl.google.com/android/eclipse/

곧 Developer Tools 라는 항목이 뜰 것이다. 체크 한 뒤에 설치한다. 혹시나 사내망에서 설치를 한다면 Proxy 설정을 해줘야 한다는 점을 명시하자.


5. SDK 설치
설치하고 나서 Eclipse를 실행시키면 SDK 가 없다고 난리를 부린다. 예전에는 SDK마저 따로 설치해 줘야 했지만 지금은 그렇지 않다. 경고들을 취소 시키고 보면 SDK를 설치 하겠냐는 창이 있다.

기본적인 설치가 끝나면 추가적인 설치가 필요하다면서 SDK Manager를 열것을 요구한다. SDK Manager를 열어서 Android 2.3.3을 설치하자.


6. Import Projects
SDK 까지 설치가 모두 끝났다면 이제 프로젝트를 가져와야 한다. 코코스 라이브러리와 안드로이드 프로젝트를 들고 와야된다. 즉, 프로젝트 2개를 가져와야 한다.

먼저 Package Explorer에서 마우스 오른쪽을 눌러 Import를 선택한다. 그리고 Root Directory 항목을 선택한 후, 우리가 미리 만들어 두었던 새로운 프로젝트를 선택하도록 한다. 선택하고 나면 엄청난 양의 프로젝트가 선택이 되어 있을 것이다. 우선 Deselect All을 선택해서 모두 선택 해제 한다.

우리에게 필요한 프로젝트는 다음과 같다.

1. cocos2d\cocos\platform\android\java
2. proj.android


7. Property Setting
libcocos2dx 프로젝트에서 마우스 오른쪽 버튼을 눌러 Properties를 선택하고 Android 항목에서 Target Name 이 Android 2.3.3을 체크하고 아래에서 Is Library를 체크한다. Okay를 눌러 저장한다.

이번에는 실제 프로젝트에서 Properties 를 선택하고 Android 에서 Target Name 이 Android 2.3.3을 체크한다. 그리고 아래의 Library 항목에서 Add를 눌러 libcocos2dx를 선택한다. 혹시 이미 들어가 있다면 Remove로 제거하고 다시 추가한다.


8. Ant 설치
아래 링크에 접속한다.

http://ant.apache.org/bindownload.cgi

Current Release of Ant 항목 조금 아래에 보면 Zip archive 라고 다운로드 링크가 있을 것이다. 다운 받아서 설치한다. 압축을 풀고 나면 ant 폴더가 하나 생성될 것인데 자신이 원하는 곳에 옮겨놓는다. 나의 경우 C:\apache-ant 로 두었다.


9. 환경 변수 설정
우리는 다음 5개를 환경변수로 설정해 주어야 한다.

1. cocos2d
2. ndk
3. android sdk
4. python
5. ant

만약 하나라도 환경변수로 등록하지 않으면 작동이 되지 않으니 환경 변수 설정을 꼼꼼히 하자. 다음의 절차를 따르자.

제어판 > 시스템 > 고급 시스템 설정 > 환경변수 > 시스템 변수

그리고 우리가 설정해야 할 환경 변수 경로가 다음과 같다면

1. cocos2d
C:\Users\deokil\Documents\cocos2d-x-3.5-tizen\tools\cocos2d-console\bin\

2. ndk
C:\Users\deokil\Documents\android-ndk-r10e\

3. android sdk
C:\Users\deokil\android-sdks\

4. python
C:\Python27\

5. ant
C:\apache-ant-1.9.6\bin\


아래의 새로 만들기 버튼을 눌러서 변수 이름과 변수 값을 다음과 같이 설정한다.

변수이름 : 변수값
NDK_ROOT : 위에 적힌 ndk 경로
COCOS_CONSOLE_ROOT : coco2d 경로
ANDROID_SDK_ROOT : sdk 경로
ANT_ROOT : ant 경로

그리고 최종적으로 Path를 수정해야 한다. 여기서 Path를 수정하는데 하나의 환경 변수를 설정하고 나면 끝에 반드시 ; 를 붙이고 다른 환경 변수의 경로를 적어줘야 한다. 예를 들자면 이미 시스템 변수로 다음과 같은 Path가 있었다고 가정하자.

C:\ProgramData\Oracle\Java\javapath

그럼 다음과 같이 적어준다.

C:\ProgramData\Oracle\Java\javapath;C:\Users\deokil\Documents\cocos2d-x-3.5-tizen\tools\cocos2d-console\bin\;C:\Users\deokil\Documents\android-ndk-r10e;
와 같은 방식으로 모두 적어주어야 한다. 명심할 것은 경로 사이에 ; 가 있다는 것이다.

(사실 이렇게까지 안해도 개발은 할 수 있지만 나중에 편해진다)


10. Android 빌드 준비
Windows Key + R 을 눌러 cmd를 입력한다. cocos2d가 설치된 폴더로 이동해서 다음의 명령어를 입력한다.

python Setup.py

만약 중간에 not found라고 뜬다면 환경 변수 설정이 잘못된 것이니 9번 항목을 다시 해보길 권한다.

이제 다시 이클립스를 켜고 프로젝트를 연다. 프로젝트 하위 폴더 중에 jni 라는 폴더가 있을 것이데 내부에 Android.mk 라는 폴더를 열어보자. 그리고 다음과 같이 수정한다.

우선 LOCAL_SRC_FILES를 다음과 같이 주석처리한다.

#LOCAL_SRC_FILES := hellocpp/main.cpp \
#                   ../../Classes/AppDelegate.cpp \
#                   ../../Classes/HelloWorldScene.cpp

그 밑에 다음의 내용을 추가 한 후 저장한다.

FILE_LIST := $(wildcard $(LOCAL_PATH)/../../Classes/*.cpp)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_SRC_FILES += hellocpp/main.cpp


11. 빌드
먼저 libcocos2dx 프로젝트를 누르고 Ctrl+B를 해서 빌드 한다. 빌드가 끝나면 게임 프로젝트를 열어 build_native.py 를 더블클릭하여 실행 한번 한다. 그리고 게임 프로젝트 또한 Ctrl+B를 눌러 빌드를 해준다.


12. 실행
프로젝트에서마우스 오른쪽 클릭해서 Run as > Android Application 을 선택해 실행한다.

p.s.
혹시나 extra qualification 에러가 발생할 수도 있다. Visual Studio는 융통성이 있어 그냥 넘어가주는 문제이지만 이클립스는 그러하지 못해서 생기는 문제다. 에러가 난 코드를 잘 보라.. 표준에 맞지 않는 에러가 있을 것이다.












[Android] UI Event Handling

1. Event Listener

1) Anonymous Inner Class
가장 일반적이고 심플한 방식의 Event 처리 방식이다. 보통 다음의 로직을 쓴다. 1회용 클래스를 넘겨준다.

btn1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Pressed btn", 0).show();
}
});

(MainActivity.this 는 MainActivity 객체를 강제로 가리키게 하는 익명 내부 클래스 코딩 방식이다)

2) NewClass implement Interface
그렇지 않고 Interface인 View.OnClickListener 을 구현하는 방식이 있다. 아래 처럼 클래스를 하나 만든다.

class MyClass implements View.OnClickListener {
public void onClick(View v) {
Toast.makeText(v.getContext(), "Pressed btn1", 0).show();
}
}

그리고 리스너를 MyClass로 넘겨준다.

btn1.setOnClickListener(new MyClass());


3) Activity implement Interface
액티비티 자체에서 리스너를 구현하는 방식이다. 다음과 같이 액티비티에서 리스너를 구현한다.

public class MainActivity extends Activity implements View.OnClickListener {
...
public void onClick(View v) {
Toast.makeText(v.getContext(), "Pressed btn1", 0).show();
}
...
}

그리고 사용할 때는 요렇게만 적어주면 된다.

btn1.setOnClickListener(this);




2. Event Handler

2015년 8월 11일

[Android] Activity Life Cycle with Debugging

DDMS 를 쓰는 것이 좋다. DDMS Perspective를 선택해서 LogCat 창의 +를 눌러 아래 항목을 채운다.

Filter name : MyFilter
by log Tag : test_tag

MainActivity를 다음과 같이 채운다.


package com.example.activitycycle;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
Log.d("test_tag", "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
protected void onDestroy() {
Log.d("test_tag", "onDestroy");
super.onDestroy();
}
protected void onPause() {
Log.d("test_tag", "onPause");
super.onPause();
}
protected void onRestart() {
Log.d("test_tag", "onRestart");
super.onRestart();
}
protected void onResume() {
Log.d("test_tag", "onResume");
super.onResume();
}
protected void onStart() {
Log.d("test_tag", "onStart");
super.onStart();
}
protected void onStop() {
Log.d("test_tag", "on");
super.onStop();
}
}

앱을 껐다 켜면서 실제로 언제 각 함수가 호출되는지 확인한다. 후후후후후후. 그림 같은거 보여주면 설명할거라 생각했다면 오산이다. 후후훟ㅎㅎㅎㅎ. 그리고 그게 가장 직관적이고 빠르게 이해가 된다.


p.s.

 혹시 로그가 안나온다면 이클립스를 껐다 켠다. 그래도 안나오면 작업관리자에서 adb.exe를 껐다가 켠다.

에뮬레이터나 실기기의 화면의 가로,세로모드를 바꿔보면 Destroy되었다가 다시 살아나는 것을 볼 수 있다.

onPause, onStop, onDestroy 함수가 호출되면 앱이 kill 될 수 있다.



[Android] Intent

Intent 는 Action, Category, Data, Component Name, Extras, Flags 로 이루어지며 컴포넌트들간의 통신에 이용된다. 또한 Intent는 명시적(Explicit) 인텐트와 암시적(Implicit) 인텐트로 나누어진다.

명시적 암시는 인자로 Component Name 을 적어준다. 예를 들면 아래와 같은 인텐트는 전화앱을 구동시키는 명시적 인텐트가 된다.

Intent intent = new Intent(Intent.ACTION_DIAL);

혹은 다음과 같이 클래스 이름을 통해 명시적으로 전달할 수도 있다.

intent.setClassName("com.example.packagename", "com.example.packagename.ActivityName");


반면 아래와 같은 인자로 Intent를 호출하게 되면 암시적인 인텐트가 된다.

Action, Category, Data : 암시적 인텐트


Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType("image/*");
startActivity(intent);


상대 컴포넌트에게 더 많은 정보를 넘겨주고 싶다면 Extras 인자를 사용하면 되고 Flags 는 안드로이드 시스템에게 제어 정보를 전달한다. 아마 Flags는 쓸 일이 없을 것이다.  다음의 코드처럼 intent에 데이터를 저장시킨다.

intent.putExtra("msg", "initial data");

그 후에 받는 쪽에서는 extra를 이용해 정보를 얻는다.

Intent intent = getIntent();
String s = intent.getStringExtra("msg"); // s = initial data


Activity, Service, Content Provider는 반드시 Manifest에 등록해야 한다. 반면 BroadcastReceiver만은 Manifest에 등록하지 않아도 된다.

[Android] BroadcastReceiver

BroadcastReceiver는 특정 사건 발생 했을 때 메시지를 받아 Activity를 띄워주는 역할이나 NotificationManager를 이용해 알림을 주는 역할을 한다. Android 4대 Component (Activity, Service, Content Provider, BroadcastReceiver) 중 하나이다.

구현을 위해 BroadcastReceiver 를 상속받으면 onReceiver(Context, Intent) 메소드를 구현해야 한다. 이 메서드는 특정 사건 발생시 안드로이드 시스템이 자동으로 호출하는 Callback 메소드다.


1. Activity를 띄우는 인텐트를 날려보자
테스트를 위해 MyReceiver라는 class를 하나 생성하고 아래와 같이 구현하자.

package com.example.receivertest;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Event", Toast.LENGTH_LONG).show();
}
}


이제 이 Receiver를 안드로이드 시스템에 존재를 알리기 위해 Manifest에 등록해줘야 한다. 또한 무슨 메시지를 받아야 할지도 알려줘야 한다. 테스트를 위해 아래와 같이 Manifest를 작성하자.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.receivertest"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="22" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="MyReceiver">
            <intent-filter>
                <action android:name="android.intent.action.LOCALE_CHANGED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

이제 등록이 되었는지 확인해볼 것인데, 위의 android.intent.action.LOCALE_CHANGED 를 등록해두면 언어가 바뀔 때 작동이 된다. 잘 되었는지 확인해 보려면 언어를 바꿔보자. 아마 Event라는 토스트 팝업을 확인할 수 있을 것이다.

방금의 예는 아주 심플하게 BroadcastReceiver가 무엇인지 알아보았다. 하지만 위의 예제에서는 본래 있던 action을 캐취하는 것에 지나지 않았다. 즉, android.intent.action.LOCALE_CHANGED 라는 미리 정의된 action을 receive 하도록 되어 있다.

사실 이 부분은 intent filter 라는 부분이라 broadcastReceiver에서 다루지 않아도 되지만 꽤나 밀접한 관련이 있다고 생각되기에 여기에 포함시켰다. 위의 xml 코드에서 intent-filter는 오직 LOCALE_CHANGED만을 캐취한다. LOCALE은 android가 미리 지정한 action 이다. 이제 우리는 user action을 만들어보자.


--보내는 쪽 앱 class 파일에서
다음처럼 버튼을 하나 만든다.

btn1 = (Button) findViewById(R.id.button1);
btn1.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent("example.test.MYACTION1");
                i.setDataAndType(Uri.parse("http://www.example.com/test.mp4"), "video/mp4");
startActivity(i);
}
});

--받는 쪽 앱 Manifest에서

<activity
     android:name=".MainActivity"
     android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <action android:name="example.test.MYACTION1" />
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="video/*" android:scheme="http"/>
    </intent-filter>
</activity>

이렇게 해두면 보내는 쪽 앱에서 버튼만 누르면 받는 쪽 MainActivity가 띄워지게 된다. 이때 보내는 쪽의 i.setDataAndType()이라는 함수는 없어도 된다. 다만 예를 들기 위해 작성한 코드다. i.setDataAndType() 함수에서 http, mp4 라는 단서를 통해서도 암시적으로 intent를 다른 앱에게 보낼 수 있을 보였다. 받는 쪽의 <data> 코드가 이것을 받겠다는 의미다.


p.s. BroadcastReceiver는 꼭 Manifest에 등록하지 않아도 된다. 즉, 동적으로 register/unregister가 가능하다.

2. BroadcastReceiver를 Manifest에 등록하기
받는쪽 Manifest에 다음 코드를 activity와 같은 레벨에서 적습니다.
<receiver android:name=".MyReceiver" >
       <intent-filter>
           <action android:name="example.MyEvent" >
           </action>
       </intent-filter>
</receiver>

보내는 쪽의 클래스 파일에서 onClick 부분을 이렇게 바꿉니다.
public void onClick(View v) {
Intent intent = new Intent("example.MyEvent");
sendBroadcast(intent);
}


3. 동적으로 BroadcastReceiver 등록/해제 하기

public class MainActivity extends Activity {

private Button btn_register, btn_unregister;
private TestReceiver receiver_obj;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btn_register = (Button) findViewById(R.id.button1);
btn_unregister = (Button) findViewById(R.id.button2);
btn_unregister.setEnabled(false);

receiver_obj = new TestReceiver();

btn_register.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
registerReceiver(receiver_obj, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
btn_register.setEnabled(false);
btn_unregister.setEnabled(true);
}
});

btn_unregister.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
unregisterReceiver(receiver_obj);
btn_register.setEnabled(true);
btn_unregister.setEnabled(false);
}
});
}
}



[Perforce] Eclipse 동기화

Perforce를 평가판이 존재한다. 다운로드하여 Virtual Box에서 서버로 돌린다.
Network의 경우 보통 브릿지로 하면 되지만 상황이 여의치 못할 경우에는 NAT로 설정한다.

이클립스에서는 Perforce plugin을 다운받아 적용시켜야 한다. 적용시키고 나면 Perspective 에 Perforce가 생긴다.

만약 새로 만든 프로젝트를 Perforce에 적용시키고 싶다면 Project에서 마우스 오른쪽 클릭해서 Team > Share Project 를 선택하면 된다. 그리고 나서 bin과 gen 폴더를 선택해 add to .p4ignore 를 선택한다. 마지막으로 프로젝트 자체를 마우스 오른쪽 클릭해서 Mark for Add를 선택하면 서버로 동기화 된다.

2015년 8월 8일

[Git] SourceTree를 이용한 개발환경 구축

1. SourceTree & Git 설치
https://www.sourcetreeapp.com/ 에서 다운로드 받아서 설치
http://git-scm.com/ 에서 다운로드 받아 설치

SourceTree를 실행시켜서 Tools > Options > Default User Information을 채워준다.
자신의 이름과 메일만 등록하면 된다. 이걸 하지 않으면 나중에 Commit 할때 에러를 띄운다.

2. GitHub 가입
http://www.github.com

3. GitHub Repository 생성
이 때, HTTPS URL을 기억해둡니다. 나중에 쓸거거든요.

4. 프로젝트를 GitHub에 업로드하기
SourceTree를 실행시킨후 왼쪽 상단의 Clone/New를 선택합니다. (저는 영어버전입니다!)
Source Path / URL 항목에는 GitHub 에서 발급받은 HTTPS URL을 입력합니다.
다음으로 Destination Path 에는 Git 저장소로 사용할 빈 폴더를 지정합니다. 그리고 나서 Clone을 하면 GitHub과 동기화가 됩니다.

동기화가 완료되었으면 우리가 조금전 만들었던 Project 폴더를 통째로 Git 저장소(Working Copy)로 옮깁니다. 폴더 자체를 옮기게 되면 그 폴더도 동기화가 되며, 아니면 내용만 카피 하셔도 됩니다.

5. SourceTree 를 이용해 Upload
이 부분은 조금 이해해야 할 부분이 많이 있습니다. 그래서 다음의 사이트로 넘어가셔서 조금 배우시고 이용하시면 될 것 같습니다.
https://opentutorials.org/course/1492/8035

어느정도 안다고 가정하고.. Push를 할때 GitHub에서 등록한 아이디와 비밀번호를 입력하면 된다.


p.s. 너무 생략해서 적어서 처음 하시는 분은 따라하기 힘들 수도 있습니다. 여기에 댓글을 달아주시거나 cdi1318@gmail.com 으로 메일주시면 답변 드릴게요 ㅎㅎㅎ



2015년 7월 29일

[Git] Git Server & Client

Server : Ubuntu 14.04 LTS
Client : Windows 7, Ubuntu 14.04 LTS

reference : https://www.davidlab.net/ko/tech/how-to-setup-git-server-on-ubuntu-part1/


- Server
1. Open SSH 설치
$ sudo apt-get install openssh-server
$ sudo ufw allow ssh

2. Git 설치
$ sudo apt-get install git-core
$ sudo adduser --system --shell /bin/bash --gecos 'Git Admin' --group --disabled-password --home /opt/git git

3.User 정보 입력
$ git config --global user.name "foo" 
$ git config --global user.email "foo@bar.com"

4. Client들의 Public Key를 등록해주기
// 이 부분은 아래의 클라이언트들이 먼저 Public, Private Key를 만들고 나서 진행해야함
받은 key의 이름이 foo.pub 이고 그것을 tmp에 넣어뒀다고 가정하면..
$ sudo su - git
$ mkdir ~/.ssh
$ chmod 700 ~/.ssh
$ cat /tmp/foo.pub >> ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys

5. Repository 생성하기
$ sudo su - git
$ mkdir test.git
$ cd test.git
$ git init --bare
$ exit

6.




- Client (Ubuntu)
1. Key 만들기
$ ssh-keygen -t rsa -C “foo@bar” 
치면 key를 생성할 폴더와 패스워드를 입력한다. 패스워드는 공란으로 두어도 된다

2. Source Import
프로젝트 폴더를 만들고 내부에 파일을 생성한다.
$ git init
$ git add .
$ git commit -m "Initial Commit"

3.Remote 추가
git@<Git Server의 IP 또는 Domain Name>:<Repository 이름>
ssh://git@<Git Server의 IP 또는 Domain Name>/<Repository 이름>
$ git remote add origin git@dev.example.com:test.git
$ git push origin master

4. Git Clone
$ git clone git@dev.example.com:test.git





 - Client (Windows)
1. Git 설치
TortoiseGit 을 설치한다. Choose SSH Client 가 나오면 TortoiseGitPlink를 선택.
MSYSGit 을 설치한다. 중간에 선택지가 나올텐데 다음과 같은 항목을 선택한다.
Use Git from The windows command prompt를 선택.
Use (Tortoise)Plink를 선택.
Checkout Windows-style, commit unix-style line ending 선택.

2. Key 만들기
Windows Key + R 를 하여 puttygen 입력하고 엔터하면 창이 뜰 것이다.
Generate 버튼을 누르고 마우스 막 휘저어라 그럼 게이지가 채워진다.
끝나면 상단에 Public Key가 생성된다. 해당 파일을 id_rsa.pub 으로 저장한다.
그리고 Save Private Key 를 눌러 자신만의 폴더에 저장해둔다. (잃어버리면 안됨)

3. Git Clone 
프로젝트 폴더를 만들고 마우스 오른쪽 클릭하고 Git Clone을 선택한다.
URL : git@dev.example.com:test.git
Directory : 프로젝트 디렉토리
Load Putty Key : 2번에서 맏들어 두었던 Private Key를 가져온다.

이제부터는 pull, push, commit 등이 가능해진다.








2015년 7월 26일

[Algorithm] Kruskal Algorithm

Kruskal 알고리즘은 MST를 만들어내는 알고리즘 하나로서 Heap과 Union-Find 라는 자료구조를 이용한다. Greedy 알고리즘이라 최적해를 도출해 내는지 알 순 없지만 누군가 증명을 했단다. MST를 이용하면 Minimum Spanning Tree 가 만들어진다.


#include <iostream>
#include <stdio.h>
#define MAX_VETICES 100
using namespace std;

// Kruskal's element
typedef struct
{
int key;
int u;
int v;
} k_elem;

// Union-Find
int parent[MAX_VETICES];
int num[MAX_VETICES];
void set_init(int n)
{
for (int i = 0; i < n; i++){
parent[i] = -1;
num[i] = 1;
}
}
int set_find(int v)
{
int p, i = -1;
for (i = v; (p = parent[i]) >= 0; i = p);
int s = i;
for (i = v; (p = parent[i]) >= 0; i = p){
parent[i] = s;
}
return s;
}
void set_union(int s1, int s2)
{
int p1 = set_find(s1);
int p2 = set_find(s2);
if (num[p1] > num[p2]){
parent[p2] = p1;
num[p1] += num[p2];
}
else{
parent[p1] = p2;
num[p2] = parent[p1];
}
}

// MinHeap
k_elem h[MAX_VETICES];
int h_size;
void h_init()
{
h_size = 0;
}
void h_insert(k_elem v)
{
int i = ++h_size;
while (i != 1 && v.key < h[i/2].key){
h[i] = h[i / 2];
i /= 2;
}
h[i] = v;
}
k_elem h_remove()
{
k_elem item = h[1];
k_elem temp = h[h_size--];
int p = 1;
int c = 2;
while (c <= h_size){
if (h[c].key > h[c + 1].key) c++;
if (temp.key <= h[c].key) break;
h[p] = h[c];
p = c;
c *= 2;
}
h[p] = temp;
return item;
}

// Kruskal
int k_distance[MAX_VETICES][MAX_VETICES] =
{
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 29, 0, 0, 0, 10, 0 }, // a
{ 0, 29, 0, 16, 0, 0, 0, 15 }, // b
{ 0, 0, 16, 0, 12, 0, 0, 0 }, // c
{ 0, 0, 0, 12, 0, 22, 0, 18 }, // d
{ 0, 0, 0, 0, 22, 0, 27, 25 }, // e
{ 0, 10, 0, 0, 0, 27, 0, 0 }, // f
{ 0, 0, 15, 0, 18, 25, 0, 0 }, // g
};


void kruskal(int n)
{
h_init();
set_init(n);
for (int i = 1; i <= n; i++){
for (int j = i; j <= n; j++){
if (k_distance[i][j] > 0){
k_elem element{ k_distance[i][j], i, j };
h_insert(element);
}
}
}

int accepted = 0;

while (accepted < (n - 1)){
k_elem e = h_remove();
int uset = set_find(e.u);
int vset = set_find(e.v);
if (uset != vset){
printf("(%d, %d) : %d\n", e.u, e.v, e.key);
accepted++;
set_union(uset, vset);
}
}
}


int main()
{
int n = 7;
kruskal(n);
return 0;
}

[Data Structure] Union Find

Union-Find는 집합을 다루는 트리 형태의 자료구조다. Union 함수를 통해 집합을 서로 결합시키고 Find를 통해 그 집합의 대표 정점을 가져온다. Union-Find 는 Kruskal 알고리즘을 구현하는 것에 사용된다. 물론 그 외에도 많이 사용된다.


#include <iostream>
#include <stdio.h>
#define MAX_VETICES 100

using namespace std;

int parent[MAX_VETICES];
int num[MAX_VETICES];

void set_init(int n)
{
for (int i = 0; i < n; i++){
parent[i] = -1;
num[i] = 1;
}
}

int set_find(int vertex)
{
int p, i = -1;
for (i = vertex; (p = parent[i]) >= 0; i = p);
int s = i;
for (i = vertex; (p = parent[i]) >= 0; i = p){
parent[i] = s;
}
return s;
}

void set_union(int s1, int s2)
{
int p1 = set_find(s1);
int p2 = set_find(s2);
if (num[p1] < num[p2]){
parent[p1] = p2;
num[p2] += num[p1];
}
else{
parent[p2] = p1;
num[p1] += num[p2];
}
}

int main()
{
// Suppose Set S = {1,2,3,4,5,6};
set_init(7);

set_union(1, 4);
set_union(5, 2);
// {1,4}, {5,2}, {3}, {6}
for (int i = 1; i <= 6; i++){
printf("Parent of [%d] : %d\n",i, set_find(i));
}

set_union(4, 5);
set_union(3, 6);
// {1,4,5,2}, {3,6}
for (int i = 1; i <= 6; i++){
printf("Parent of [%d] : %d\n", i, set_find(i));
}
return 0;
}

2015년 7월 25일

[Data Structure] Topological Order (위상 정렬)

위상 정렬. 순회가 없고 방향이 있는 그래프(Directed acyclic graph)를 선후관계에 의해 정렬하는 방법이다. Stack을 이용해 구현을 하긴 했지만 다른 자료구조를 이용해도 문제는 없을 것으로 보인다. 포인터를 사용하지 않고 구현하려 애써봤지만 link 때문에 포인터가 필요하며, 그것 때문에 각 노드를 malloc 해줘야 했다. 이 이상 단순하게 짤 수 있을지 모르겠다.


#include <iostream>
#include <stdio.h>

using namespace std;

int s[100];
int s_size;
void init_s()
{
s_size = -1;
for (int i = 0; i < 100; i++){
s[i] = NULL;
}
}
int pop()
{
return s[s_size--];
}
void push(int i)
{
s[++s_size] = i;
}
int is_empty_s()
{
return s_size == -1;
}


#define MAX_NODE_NUM 100

typedef struct GraphNode
{
int vertex;
struct GraphNode *link;
} GraphNode;

int g_size;
GraphNode *adj_list[MAX_NODE_NUM];
int in_degree[MAX_NODE_NUM];

void graph_init(int n)
{
g_size = n;
for (int v = 0; v < MAX_NODE_NUM; v++){
adj_list[v] = NULL;
}
}

void insert_edge(int u, int v)
{
GraphNode *node = (GraphNode*)malloc(sizeof(GraphNode));
node->vertex = v;
node->link = adj_list[u];
adj_list[u] = node;
}

void topo_sort()
{
for (int i = 0; i < g_size; i++){
GraphNode *node = adj_list[i];
while (node){
in_degree[node->vertex]++;
node = node->link;
}
}
init_s();
for (int i = 0; i < g_size; i++){
if (in_degree[i] == 0) push(i);
}
while (!is_empty_s()){
int w = pop();
printf("%d ", w);
GraphNode *node = adj_list[w];
while (node){
in_degree[node->vertex]--;
if (in_degree[node->vertex] == 0) push(node->vertex);
node = node->link;
}
}
return;
}

int main()
{
graph_init(6);
insert_edge(0, 2);
insert_edge(0, 3);
insert_edge(1, 3);
insert_edge(1, 4);
insert_edge(2, 3);
insert_edge(2, 5);
insert_edge(3, 5);
insert_edge(4, 5);
topo_sort();
return 0;
}

2015년 7월 21일

[Algorithm] 알고리즘 정리

Algorithm

1. Basic : Complexity, Bit
2. Brute Force & Complete Searching
3. Greedy
4. Divide and Conquer
5. Back Tracking
6. Graph (MST, DSP, BSP, etc,.)
7. String
8. Dynamic Programming
9. Math (특히 이산수학, 정수론)
10. Probability

[Data Structure] 자료구조 정리

Data Structure

1. Array
2. List : Linked List, Circular Linked List, Double Linked List
3. Stack
4. Queue : Queue, Deck
5. Tree : Binary Tree, Thread Binary Tree
6. Heap (Priority Queue)
7. Sorting : Selection, Insertion, Bubble, Cell, Merge, Quick, Heap, Radix(기수)
8. Graph : DSP, BSP, MST, Shortest Path, Topological Sort
9. Hashing : Hash Function, Linear Probing, Chaining
10. Searching : Searching in sorted data, Searching in not sorted data, AVL Tree


2015년 7월 19일

[Algorithm] Repeated Squaring

Repeated Squaring은 a의 p승을 log 시간에 계산하는 알고리즘이다.
define 된 MOD 는 알고리즘 풀 때 너무 큰 값이 나오는 것을 방지하기 위한 모듈러 값이다.

알고리즘 이해를 해보자.
모든 p는 2의 배수로 나타낼수 있다. 12를 소인수분해 한 나머지를 생각해보자.
8(2의3승) + 4(2의2승) 으로 나타낼 수 있다. 그외의 모든 수가 그러하다. 왜냐하면 모든 수가 2진수로의 변환이 가능하기 때문이다. 12를 소인수 분해한 나머지를 나열해보면 1100이 나온다. 이게 12의 2진수를 의미한다. 그리고 그때마다 해당하는 2의 승수를 곱해주면 된다.

이해가 안갈테니 3의 36승이 있다고 하자. 이 때 36을 소인수분해 후 나머지를 나열해보면
100100 이 나온다. 이 값은 2의 5승 + 2의 2승이며 각각 32와 4이다.
이 때 1의 값이 나올 때의 2의 승수를 계속 곱해주면 값이 나오는 것이다. 이해하기 어려울 것인데....ㅜㅜ

아래 코드를 참고해서 말해 보면.

    a         p       v
3의1승    36      1
3의2승    18      1
3의4승     9       1
3의8승     4    3의4승
3의16승   2    3의4승
3의32승   1    3의4승
3의64승   0    3의4승 * 3의 32승

맨마지막의 v값이 결과다.


#include <stdio.h>
#include <iostream>

#define MOD 1000000007

using namespace std;

int pow(int a, int p)
{
int val = 1;
while (p > 0){
if (p % 2 == 1) val = (val * a) % MOD;
p /= 2;
a = (a *a) % MOD;
}
return val;
}

int main()
{
long long int ret = pow(2, 20);
printf("%lld\n", ret);
return 0;
}