아빠는 개발자

이름 궁합 계산기

June 06, 2019

내가 30년 전 초등학교 다닐 적에 하던 놀이 중에 아직도 남아있는 것들이 있다. 이름의 획 수를 가지고 궁합을 계산하는 것도 그중의 하나인데, 아들과 함께 코드로 작성해보았다.

데모

입력 창에 두 개 이상의 이름을 입력하시면 각 이름 간의 궁합을 확인할 수 있습니다. (한글 이름만 가능합니다.)


코드

입력받은 이름의 각 글자의 획수를 알아내는 부분이 조금 생소할 뿐, 점수를 계산하는 방법은 단계를 거치면서 합산한 결과를 10으로 나눈 나머지를 특정 조건이 될 때까지 반복하면 된다.

초성, 중성, 종성 분리

아래와 같이 코드를 작성해서 실행해보면 UTF-16 코드 상에서 한글이 어떤 순서로 배열되어 있는지 짐작할 수 있다.

const BASE = '가'.charCodeAt(0); // 한글 코드 시작: 44032
Array(100)
.fill()
.map((e, i) => BASE + i)
.map((i) => String.fromCharCode(i))
.join(' ');
// 가 각 갂 갃 간 갅 갆 갇 갈 갉 갊 갋 갌 갍 갎 갏 감 갑 값 갓 갔 강 갖 갗 갘 같 갚 갛
// 개 객 갞 갟 갠 갡 갢 갣 갤 갥 갦 갧 갨 갩 갪 갫 갬 갭 갮 갯 갰 갱 갲 갳 갴 갵 갶 갷
// 갸 갹 갺 갻 갼 갽 갾 갿 걀 걁 걂 걃 걄 걅 걆 걇 걈 걉 걊 걋 걌 걍 걎 걏 걐 걑 걒 걓
// 걔 걕 걖 걗 걘 걙 걚 걛 걜 걝 걞 걟 걠 걡 걢 걣 ...

이를 바탕으로 초성, 중성, 종성을 분리하고, 각각의 획 수를 반환하는 함수를 작성하였다. (각 자소의 획은 직접 세어 객체로 만들어두었다.)

const BASE = '가'.charCodeAt(0); // 한글 코드 시작: 44032
const INITIALS = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];
const MEDIALS = ['ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'];
const FINALES = ['', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];
const STROKES = { '': 0, 'ㄱ': 2, 'ㄲ': 4, 'ㄴ': 2, 'ㄷ': 3, 'ㄸ': 6, 'ㄹ': 5, 'ㅁ': 4, 'ㅂ': 4, 'ㅃ': 8, 'ㅅ': 2, 'ㅆ': 4, 'ㅇ': 1, 'ㅈ': 3, 'ㅉ': 6, 'ㅊ': 4, 'ㅋ': 3, 'ㅌ': 4, 'ㅍ': 4, 'ㅎ': 3, 'ㄳ': 4, 'ㄵ': 5, 'ㄶ': 5, 'ㄺ': 7, 'ㄻ': 9, 'ㄼ': 9, 'ㄽ': 7, 'ㄾ': 9, 'ㄿ': 9, 'ㅀ': 8, 'ㅄ': 6, 'ㅏ': 2, 'ㅐ': 3, 'ㅑ': 3, 'ㅒ': 4, 'ㅓ': 2, 'ㅔ': 3, 'ㅕ': 3, 'ㅖ': 4, 'ㅗ': 2, 'ㅘ': 4, 'ㅙ': 5, 'ㅚ': 3, 'ㅛ': 3, 'ㅜ': 2, 'ㅝ': 4, 'ㅞ': 5, 'ㅟ': 3, 'ㅠ': 3, 'ㅡ': 1, 'ㅢ': 2, 'ㅣ': 1 };
function getSymbol(char) {
if (!char.match(/[ㄱ-ㅎ가-힣]/)) {
return false;
}
let initial = '';
let medial = '';
let finale = '';
if (char.match(/[ㄱ-ㅎ]/)) {
initial = char;
} else {
const tmp = char.charCodeAt(0) - BASE;
const finaleOffset = tmp % FINALES.length;
const medialOffset = ((tmp - finaleOffset) / FINALES.length) % MEDIALS.length;
const initialOffset = ((tmp - finaleOffset) / FINALES.length - medialOffset) / MEDIALS.length;
initial = INITIALS[initialOffset];
medial = MEDIALS[medialOffset];
finale = FINALES[finaleOffset];
}
const initialStrokes = STROKES[initial];
const medialStrokes = STROKES[medial];
const finaleStrokes = STROKES[finale];
return {
initial,
medial,
finale,
initialStrokes,
medialStrokes,
finaleStrokes,
numOfStrokes: initialStrokes + medialStrokes + finaleStrokes,
};
};

스코어 계산

각 글자의 획수를 알 수 있으면 숫자 궁합 점수는 아래와 같이 쉽게 계산할 수 있다.

function getScore(name1, name2) {
const symbols1 = name1.match(/[\s\S]/g).map((char) => [char, getSymbol(char).numOfStrokes]);
const symbols2 = name2.match(/[\s\S]/g).map((char) => [char, getSymbol(char).numOfStrokes]);
const maxLen = Math.max(symbols1.length, symbols2.length);
const [chars, numbers] = Array(maxLen)
.fill()
.reduce(
([accum1, accum2], e, i) => {
const [char1, num1] = symbols1[i] || ['', 0];
const [char2, num2] = symbols2[i] || ['', 0];
return [[...accum1, char1, char2], [...accum2, num1, num2]];
},
[[], []],
);
let nums = numbers.slice();
const stages = [nums];
while (nums.length > 2 && nums.join('') !== '100') {
nums = nums.reduce((a, e, i, arr) => {
if (i < arr.length - 1) {
return [...a, (e + arr[i + 1]) % 10];
}
return a;
}, []);
stages.push(nums);
}
return { chars, stages, score: ~~stages.slice(-1)[0].join('') };
}
// 배성재, 장예원 →
// {
// "chars": ["배", "장", "성", "예", "재", "원"],
// "stages":[
// [7, 6, 5, 5, 6, 7],
// [3, 1, 0, 1, 3],
// [4, 1, 1, 4],
// [5, 2, 5],
// [7, 7]
// ],
// "score": 77
// }

Chord Diagram

데이터를 행렬(Matrix)로 변경하면 D3.js를 사용하여 Chord Diagram을 그릴 수 있다.

{
"name1": "공효진", "name2": "차태현",
"score1": 94, "score2": 7
},
{
"name1": "공효진", "name2": "김수현",
"score1": 17, "score2": 35
},
{
"name1": "차태현", "name2": "김수현",
"score1": 9, "score2": 14
}
]
[
[0, 94, 17],
[7, 0, 9],
[35, 14, 0]
]

Edit on GitHub


개발자를 꿈꾸는 아들을 둔 아빠 개발자입니다.
데이터 시각화에 관심이 있으며, 재미있는 프로그램을 만드는 것을 좋아합니다.