계산기 2.0

※맵 다운로드
1. 계산기 2.0.zip




사실은 계산기 1.0처럼 만드려고 했는데 귀찮은 것은 물론이고 다른 할것도 많아서 고급 인터페이스따윈 다갖다 버리고
에디터에서 값을 입력한대로 연산을 수행하여 결과를 보여주는 형태로 만들어 올립니다.
그대신 트리거를 복붙하면 다른 맵에서 이 연산트리거를 이용할 수 있습니다......만
엄청난 트리거 갯수와 연산속도는 책임못져요. ㅇㅅㅇ 느긋하게 연산이 수행되도 문제없는 경우에 사용하세요.
(아 물론 연산종류에 따라 빠른건 엄청빠릅니다.(덧셈이나 뺄셈과 같은것...)
아 그리고 당연히 여기서 사용되는 연산은 모두 0을 포함한 자연수 범위 내에서만 계산됩니다.
소숫점 이런건 다 버려버리고 오직 unsigned int 타입(0을 포함한 자연수)의 입출력만 합니다.


우선 맵을 열어보면 플레이어1, 2 와 All players의 트리거가 있는데 플레이어1, 2 의 트리거는 연산테스트용이고 All players에
있는 트리거가 실질적으로 연산을 수행하는 트리거입니다. 별로 중요하진 않지만(?) 트리거 갯수를 프로그램돌려서
세어 봤는데 다음과 같은 결과가 나옵니다.

sum : 1723
"All players" : 1650
"Player 1" : 70
"Player 2" : 3

무려 연산 부분만 1650개의 트리거가 사용되었습니다... -_-;; 다른 맵에서 연산 트리거를 사용하시려면 All players의 트리거만
복붙하면 됩니다.


이 연산 트리거에는 총 12개의 셋데스가 사용되었습니다.(흐얽.. 좀 부담이 되긴될듯..) 목록은 다음과 같습니다.

(왼쪽은 제작중 편의를 위해 붙인 변수명)
R : Uraj Crystal
r : Khaydarin Crystal
T : Data Disc
t : Khalis Crystal
A : Vespene Orb (Protoss Type 1)
a : Vespene Orb (Protoss Type 2)
B : Vespene Tank (Terran Type 1)
b : Vespene Tank (Terran Type 2)
C : Mineral Chunk (Type 1)
c : Mineral Chunk (Type 2)
X : Psi Emitter
x : Flag


이중에서 조작해야 할 변수들은 입출력을 담당하는 변수 5개 입니다.

계산값입력1 - A : Vespene Orb (Protoss Type 1)
계산값입력2 - B : Vespene Tank (Terran Type 1)
연산종류입력 - C : Mineral Chunk (Type 1)
계산결과1 - X : Psi Emitter
계산결과2 - x : Flag

이 외 7개의 데스값은 계산중에 사용되는 변수이며, 외부에서 조작할 필요도 없고 해서도 안됩니다.
만일 이 연산 트리거를 붙여넣으려는 맵에 위의 데스값이 이미 사용되고 있다면 붙여넣기 전 SCMDraft2의
텍스트 트리거 편집기와 메모장의 바꾸기 기능을 사용하여 데스값이 중복사용되지 않도록 한 뒤 붙여넣습니다.

이 연산 트리거의 사용 방법은 간단합니다.
계산입력값인 A와 B에 연산할 값을 집어넣습니다. 그리고 마지막으로 원하는 연산의 코드를 C에다가 집어넣으면
연산 결과가 X와 x에 리턴됩니다. 물론 연산 종류에 따라 입력값이나 출력값이 1개일 수도 있습니다.


C에 집어넣을 연산 코드들은 다음과 같습니다.

0 : ready (연산중이 아니라는 의미로, 연산이 완료되었다는 의미이기도 합니다.)
1 : reset (계산중 문제가 생기거나 기타 등등 이유로 인해 12개의 셋데스를 모두 0으로 초기화 합니다.)
2 : + (덧셈을 합니다.)
3 : -, >, =, < (음수연산이 안되는 관계로 뺄셈이 아닌 차를 계산하며 크기 비교도 합니다.)
4 : * (곱셈을 합니다.)
5 : /, % (몫과 나머지를 계산합니다.)
6 : √ (제곱근을 계산합니다.)
7 : ^ (지수계산을 합니다.)
8 : ! (팩토리얼을 계산합니다.)
9 : nPr (순열을 계산합니다.)
10 : nCr (조합을 계산합니다.)
11 : rand()%n; (0≤r<n 인 난수 r을 구합니다.)




연산 종류의 특징입니다.

1. 덧셈
입력값 A와 B를 더해 X에 리턴합니다.
x에는 그냥 0이 리턴되며
계산속도는 일정하고 빠릅니다.

2. 뺄셈 및 비교연산
입력값 A와 B의 차를 계산하여 X에 리턴합니다.
x에는 비교값이 리턴되는데
A가 B보다 클 경우 1
A와 B가 같을 경우 2
B가 A보다 클 경우 3
이 리턴됩니다.
계산속도는 덧셈과 마찬가지로 일정하면서 빠릅니다.

3. 곱셈
입력값 A와 B를 곱하여 X에 리턴하며
x에는 0이 리턴됩니다.
계산 속도는 A와는 무관하며 B가 2의 누승(B=2^n, n은 0을 포함한 자연수)일 경우 덧셈과 동일한 속도로 계산됩니다.
(B를 이진수로 변환하였을 때 1의 수가 많을수록 계산이 느려집니다.)

4. 나눗셈 및 나머지연산
A/B의 값을 정수화(소숫점 이하 버림)하여 X에 리턴합니다.
x에는 나머지값이 리턴됩니다.
계산속도에 영향을 미치는 요소가 많아 정확하게 한가지로 추릴 순 없지만
일반적으로 몫이 작고 B를 이진수로 변환하였을 때 1의 수가 적을수록 계산속도가 빠릅니다.
(내부적으로 곱셈연산을 사용합니다.)

5. 제곱근
A의 제곱근을 계산하여 X에 리턴하며 B에 입력한 값은 무시됩니다.
x에는 0이 리턴됩니다.
나눗셈이랑 동일안 알고리즘을 사용했기 때문에 결과 값이 작을수록 연산이 빠른 경향을 보입니다.
(B값은 이용되지 않기 때문에 B값과 계산속도는 무관합니다.)

6. 지수계산
A^B를 계산하여 X에 리턴합니다.
x에는 0이 리턴됩니다.
A를 반복하여 곱하기 때문에 A를 이진수로 변환하였을때 1의 수가 적을수록 계산속도가 빠릅니다.
또한 B번 반복 계산하기 때문에 B의 값이 적을수록 계산속도가 빠릅니다.

7. 팩토리얼
A!을 계산하여 X에 리턴하며 B에 입력한 값은 무시됩니다.
x에는 0이 리턴됩니다.
A의 값이 작을수록 계산이 빠릅니다.

8. 순열
APB를 계산하여 X에 리턴합니다.
x에는 0이 리턴됩니다.
B값이 작을수록 계산이 빠른 경향성을 보입니다.
(A값과의 상호작용에 따라 빨라질수도 있고 느려질수도 있습니다.)

9. 조합
ACB를 계산하여 X에 리턴합니다.
x에는 0이 리턴됩니다.
A값이 작을수록, A값과 B값의 차가 적을수록 빠른 경향성을 보입니다.
(내부적으로 순열, 팩토리얼, 나눗셈 연산을 사용합니다.)
ACB=AC(A-B)이 성립하는데 B와 (A-B)중 큰 값을 입력하면 트리거 한주기(1/12초)만큼 시간이 더 걸립니다.

10. 난수생성
0≤r<A인 난수 r을 구해 X에 리턴하며, B에 입력한 값은 무시됩니다.
x에는 0이 리턴됩니다.
내부적으로 범위에 맞지 않으면 다시 난수를 구하는 연산을 반복하기 때문에
연산 시간은 랜덤이며, 일반적으로 A가 2의 누승(A=2^n, n은 0을 포함한 자연수)일때 반복 계산할 확률이 0이 되므로
가장 계산이 빠릅니다.




연산에 사용된 알고리즘을 간단하게 보면

1. 곱셈
A와 B를 2의 누승단위로 분할합니다. ex) A가 10일때 A=2+8
이렇게 분할된 두 수의 곱을 식으로 표현해보면
(a0+a1+ … +an) * (b0+b1+ … +bN)이 되는데 이 식은 분배법칙에 의해
a0*b0 + a0*b1 + … + an*bN이 됩니다. 여기서 2의 누승끼리의 곱이 2^31을 초과하지 않는 범위 내에서 모든
2의 누승끼리의 곱 결과를 미리 트리거로 작성해 둡니다.(트리거가 많이 필요하죠...)
미리 저장해둔 결과들 중 필요한 곱의 결과만 가져와 합산하면 곱셈계산이 완료됩니다.

2. 나눗셈(제곱근)
우선 몫(제곱근의 경우 결과값)이 0~1범위 내에 있다는 가정을 하고 연산을 시작합니다.
1을 몫으로 두고 검산을 하는데 이때 A값과 비교를 합니다.
A값이 더 작다면 몫은 0(정수니까 소수점은 무시)일것이고 A값과 같다면 몫은 1일 것이며
A값이 더 크다면 몫은 1 이상이 되겠죠. A가 더 크다면 처음 범위를 두배합니다.
0~1이었으니 1~2로 구간을 정하고 2를 몫으로 두고 검산을 하고 A와 비교합니다.
이런 식으로 예상한 몫과 B의 곱이 A보다 커질때까지 구합니다.
그러면 2^n ≤ (구하고자 하는 몫) ≤ 2^(n+1) 인 상태로 만들 수 있습니다.
이제부터는 구하고자 하는 몫의 범위 양끝값을 더하고 반으로 나눠 검산을 진행합니다.
이 과정을 몫의 양끝값의 차이가 1이 될때까지 하면 몫을 구할 수 있습니다.
물론 검산 중간에 딱 떨어지는 값이 나오면 바로 몫을 리턴합니다.
(제곱근의 경우 B와 예상한 몫을 곱하는 대신 예상한 결과값을 제곱하는 연산을 수행하도록 바꾸면 동일한 알고리즘을
이용하여 구해낼 수 있습니다.)
이 방식은 흔히 알고 있는 그 100페이지짜리 책에서 n쪽을 찾을때 먼저 반인 50페이지와 n을 비교해서 더 큰가 작은가를
보고 크면 50~100, 작으면 0~50 이런식으로 범위를 줄여나가 답을 찾는 방식을 응용한 것입니다.
적어도 몫이 커지는 계산에서는 단순히 뺄셈 계산만 하는것보다는 빠를것입니다.(적어도 유즈맵에선...)

3. 나머지 연산들
그냥 기본적인 덧셈, 뺄셈(비교), 곱셈, 나눗셈을 정의대로 순차적으로 연산한것입니다.
아 조합 계산할때 B값과 (A-B)값중 작은 값을 선택해 넣어 속도에 유리하게 만든게 하나더 있긴 있지만
간단하니 이정도로 설명 끝내도록 하겠습니다.




제공되는 예제맵의 몇가지 부가기능들(별거없어요)

1. 계산이 완료되면 계산이 완료되었다는 텍스트 메시지가 뜨며, x값이 미네랄, X값이 가스에 출력됩니다.

2. Custom스코어 리더보드는 계산을 하는데 소요된 트리거 주기를 표시합니다.

※덧셈이나 뺄셈같은 경우에는 원래 1주기면 충분하지만 다른 연산에 내부적으로 이용되다보니 트리거 동작순서 등의
이유로 2주기가 걸리게 되네요...




P.S. 얼마나 실용성이 있을지는 미지수...


P.S.2 아 오류가 있다면 어떤 값을 가지고 어떤 연산을 했을때 오류가 났었다 라는 내용을 댓글이나
ajw9105@hanmail.net
으로 보내주시면 되겠습니다.~(루퍼라는 프로그램을 돌려 만든거라 왠만해선 오류가 없다고 생각되지만...)


P.S.3 새삼스럽게 루퍼라는 프로그램을 만드신 jin3님에게 감사를 표합니다.
(루퍼없이 트리거가 700개인 계산기 1.0을 만들때 한달이 걸렸죠...)


P.S.4 아 깜빡할뻔 했는데 일단 unsigned int타입에서 최대로 큰 수를 계산할 수 있도록 되어 있습니다.
하지만 알고리즘이나 계산과정에 따라 각 계산마다 조금씩 최대로 계산가능한 수가 다른데 아마도
2^30(입력값, 출력값 모두)까지는 무난하게 계산될듯 싶습니다.(아마도요...)




noname01이 제작한 다른 맵을 보려면 이 이미지를 클릭해 주세요.