이제는 접은 메이플스토리M...
2년전에 메이플스토리M 게임을 하다가,
자꾸 너무 말도 안되는 확률로 무기가 여러번 깨지자
도대체 이 게임의 확률은 조작인가 아닌가를 밝혀내기 위해서 시뮬레이션 프로그램을 만들었었다.
개인적인 결론은 확률 조작이었지만,
고객사에 물어보니 당연히 아니라고 했다.
지금도 잘 동작 하는지 모르겠지만, 여튼 아래 코드를 공개한다.
먼저 설명이다.
1. 메이플의 아이템 강화 방식 소개
ㆍ아이템 강화에 성공하면 "별" 이 하나 증가하면서 능력치가 상승하는 시스템이다.
아이템 강화에 실패하면 이후 두가지 케이스로 나뉜다.
ㆍ희박한 확률로 깨지거나, 비교적 높은 확률로 별이 하락한다.
대신에 강화할 때 두가지 기능을 사용할 수 있다.
하나는 주문서이고, 다른 하나는 스타캐치이다.
ㆍ주문서는 럭키데이 / 하락방지 / 파괴방지로 나뉘며 사용시 각각
강화 확률 증가 / 별 하락 확률 0% / 파괴될 확률 0% 가 된다.
이 주문서는 따로 사용해도 되고 같이 사용해도 되는데 매우 비싸다.
ㆍ스타캐치는 강화시 할 수 있는 타이밍 맞추기 미니 게임이다.
사용자가 타이밍을 잘 맞추면 강화 확률 +5% 가 추가로 주어진다.
2. 시뮬레이션 프로그램
수 많은 사람들이 아이템 "강화" 에 도전하는 시나리오를 가상으로 시뮬레이션 한다.
ㆍ사람들은 각각 주어진 주문서를 갖고 시작별에서 시작하여 목표별로 강화를 도전한다.
ㆍ동일인이라도 각 강화마다 스타캐치는 확률에 따라 일어난다
(만약 스타캐치를 항상 성공할 자신이 있는 경우 100 을 입력한다.)
ㆍ주문서가 없는 경우나 주문서가 있어도 현재 5별 이하이면 주문서 없이 강화한다.
ㆍ각 주문서는 아끼다가 목표치에 가까워지면 일제히 사용한다.
( 목표까지 3별 남았다면 주문서가 3개 이상 있을때만 사용 )
( 2별 남았는데 럭데 1개, 하방 2 개, 파방 3개 있으면 럭데는 아끼고 하방, 파방 사용 )
ㆍ상세 로그를 켜지 않으면 최종 리포트만 출력한다.
(가장 운 좋은 사람의 경우, 평균적인 경우, 가장 운 나쁜 사람의 경우가 나온다)
ㆍ로그를 켜면 어떻게 해당 별에 도달하게 되었고 언제 주문서를 썼는지 자세히 확인 가능하다.
3. 결과
수십만명의 사람으로 시도해본 결과, 내가 겪은 케이스를 똑같이 겪은 사람은
몇십명 밖에 되지 않았다. 그러니 이론상 나는 수만분의 1확률로 극악의 케이스를 겪은 행운의 사나이다.
하지만 내 주위에 이 게임을 하는 사람들이 많았는데, 똑같이 또는 더 심한 극악의 케이스를 자주 겪곤 했다.
판단은 여러분께 맡긴다.
/* Copyright (c) 2018 admin@securekim.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 강화 계산기 1. 총 몇명이 도전할 지 선택. 2. 시작별 입력 3. 목표별 입력 4. 스타캐치 성공 확률 입력 (0~100) 5. 각 줌서 (럭데, 하방, 파방) 갯수 입력 6. 상세 로그 선택 */ #include #include #include #include #include using namespace std; int REINFORCE[21][4] = { { 0, 0, 0, 0 }, //STAR 0 { 95, 5, 0, 0 }, //STAR 1 { 90, 10, 0, 0 }, //STAR 2 { 85, 15, 0, 0 }, //STAR 3 { 80, 20, 0, 0 }, //STAR 4 { 75, 25, 0, 0 }, //STAR 5 { 65, 15, 15, 5 }, //STAR 6 { 60, 20, 15, 5 }, //STAR 7 { 55, 25, 15, 5 }, //STAR 8 { 50, 30, 15, 5 }, //STAR 9 { 45, 35, 15, 5 }, //STAR 10 { 35, 45, 15, 5 }, //STAR 11 { 30, 50, 15, 5 }, //STAR 12 { 25, 55, 15, 5 }, //STAR 13 { 20, 60, 15, 5 }, //STAR 14 { 15, 65, 15, 5 }, //STAR 15 { 10, 70, 15, 5 }, //STAR 16 { 7, 73, 15, 5 }, //STAR 17 { 5, 75, 15, 5 }, //STAR 18 { 3, 77, 15, 5 }, //STAR 19 { 1, 79, 15, 5 } //STAR 20 }; enum{ MYNULL, //0 BLUE, //1 GREEN, //2 MAGENTA, //3 RED, //4 PEACH, //5 YELLOW, //6 WHITE, //7 GRAY, //8 LBLUE, //9 LGREEN, //10 LMAGENTA, //11 LRED, //12 LPEACH, //13 LYELLOW, //14 LWHITE //15 }; HANDLE hConsole; int cntsum; int cnt; int first = 0, last = 0, start = 0, result = 0; int LOG = 0; int myresult[4]; int EVERAGE; int GOODLUCK = 999999; int BADLUCK = 0; int STARCATCH; //사용한 갯수 int LUCKYDAY; int SAFETY; int PROTECT; //총 결과 int R_LUCK[4]; int R_BAD[4]; int R_EVER[4]; enum{ PASS, NORM, FAIL, DEST }; void init(){ cnt = 0; start = first; for (int i = 0; i < 4; i++){ myresult[i] = 0; } } int goReinforce(int NEXT_RATING, int starcatch, bool luckyday, bool safety, bool protect) // starcatch / luckyday / safety / protect // +5 -5 0 0 / +10 -10 0 0 / 0 +15 -15 0 / 0 +5 0 -5 // 0 ~ 5 성까지는 사용하지 않도록 강제 // 현재가 5성 이상일때 부터 마지막 목표치 이전에 남아 있는 경우 사용. // starcatch 확률 입력 가능 { double r = rand() / (double)RAND_MAX; //{0.0 - 1.0} double dr = r * 100.0f; // {0.0 - 100.0} double r2 = rand() / (double)RAND_MAX; //{0.0 - 1.0} double dr2 = r2 * 100.0f; // {0.0 - 100.0} NEXT_RATING = NEXT_RATING > 20 ? 20 : NEXT_RATING; int TMP_REINFORCE[4]; TMP_REINFORCE[PASS] = REINFORCE[NEXT_RATING][PASS]; TMP_REINFORCE[NORM] = REINFORCE[NEXT_RATING][NORM]; TMP_REINFORCE[FAIL] = REINFORCE[NEXT_RATING][FAIL]; TMP_REINFORCE[DEST] = REINFORCE[NEXT_RATING][DEST]; double cumulative = 0.0f; cumulative += starcatch; if (dr2 <= cumulative){ if (LOG > 0){ SetConsoleTextAttribute(hConsole, MAGENTA); cout << "[ USE ] STAR CATCH +5%" << endl; } TMP_REINFORCE[PASS] += 5; TMP_REINFORCE[NORM] -= 5; } if (luckyday){ TMP_REINFORCE[PASS] += 10; TMP_REINFORCE[NORM] -= 10; } if (safety){ TMP_REINFORCE[NORM] += 15; TMP_REINFORCE[FAIL] -= 15; } if (protect){ TMP_REINFORCE[NORM] += 5; TMP_REINFORCE[DEST] -= 5; } TMP_REINFORCE[PASS] = TMP_REINFORCE[PASS] > 100 ? 100 : TMP_REINFORCE[PASS]; TMP_REINFORCE[NORM] = TMP_REINFORCE[NORM] > 100 ? 100 : TMP_REINFORCE[NORM]; TMP_REINFORCE[NORM] = TMP_REINFORCE[NORM] < 0 ? 0 : TMP_REINFORCE[NORM]; TMP_REINFORCE[FAIL] = TMP_REINFORCE[FAIL] < 0 ? 0 : TMP_REINFORCE[FAIL]; TMP_REINFORCE[DEST] = TMP_REINFORCE[DEST] < 0 ? 0 : TMP_REINFORCE[DEST]; cumulative = 0.0f; for (int i = 0; i<4 br="" i=""> { cumulative += TMP_REINFORCE[i]; if (dr <= cumulative) { return i; } } } int main(){ hConsole = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hConsole, MAGENTA); srand(time(NULL)); int TOTAL; cout << endl; cout << "======================================" << endl; cout << " Reinforce calculator ver 1.0" << endl < cout << " LAST RELEASE : 2018. 03. 20" << endl; cout << " DEVELOPER : UNION / 쇠가시" << endl; cout << " CONTACT : admin@securekim.com" << endl; cout << "======================================" << endl; cout << endl; SetConsoleTextAttribute(hConsole, WHITE); int my_starcatch; int my_luckyday; int my_safety; int my_protect; int tmp; int tmp_luckyday; int tmp_safety; int tmp_protect; bool flag_luckyday; bool flag_safety; bool flag_protect; // starcatch / luckyday / safety / protect cout << "TOTAL PEOPLE :" << endl; cin >> TOTAL; cout << "START STAR :" << endl; cin >> first; cout << "LAST STAR :" << endl; cin >> last; cout << "STAR CATCH SUCCESS PERCENT (0 ~ 100):" << endl; cin >> my_starcatch; cout << "How many LUCKYDAYs do you have ? :" << endl; cin >> my_luckyday; cout << "How many SAFETYs do you have ?:" << endl; cin >> my_safety; cout << "How many PROTECTs do you have ?" << endl; cin >> my_protect; cout << "Get Detail Log ? (YES : 1, NO :0) :" << endl; cin >> LOG; cout << "[START] " << first << " ------> " << last << endl; for (int TC = 1; TC <= TOTAL; TC++) // 한사람 { init(); if (LOG > 0){ cout << "=== [ DETAIL LOG ] " << first << " ---> " << last << endl; } tmp_luckyday = my_luckyday; tmp_safety = my_safety; tmp_protect = my_protect; while (start < last) // 한번 완성되는 경우 { cnt++; cntsum++; flag_luckyday=0; flag_safety=0; flag_protect=0; if (start >= 5 && tmp_luckyday > 0 && tmp_luckyday >= (last - start)){ tmp_luckyday--; flag_luckyday = 1; if (LOG > 0){ SetConsoleTextAttribute(hConsole, PEACH); cout << "[ USE ] " << "LUCKY DAY SHEET " << tmp_luckyday + 1 << " -> " << tmp_luckyday << endl; } } if (start >= 5 && tmp_safety > 0 && tmp_safety >= (last - start)) { tmp_safety--; flag_safety=1; if (LOG > 0){ SetConsoleTextAttribute(hConsole, PEACH); cout << "[ USE ] SAFETY SHEET " << tmp_safety + 1 << " -> " << tmp_safety << endl; } } if (start >= 5 && tmp_protect > 0 && tmp_protect >= (last - start)) { tmp_protect--; flag_protect=1; if (LOG > 0){ SetConsoleTextAttribute(hConsole, PEACH); cout << "[ USE ] " << "PROTECT SHEET " << tmp_protect + 1 << " -> " << tmp_protect << endl; } } result = goReinforce(start + 1, my_starcatch, flag_luckyday, flag_safety, flag_protect); myresult[result]++; if (result == PASS){ if (LOG > 0){ SetConsoleTextAttribute(hConsole, LMAGENTA); cout << " [ "<< cnt <<" - PASS ] " << start << " ---> " << start + 1 << endl; } start++; } else if (result == NORM){ if (LOG > 0){ SetConsoleTextAttribute(hConsole, YELLOW); cout << " [ " << cnt << " - NORM ] " << start << " ---> " << start << endl; } } else if (result == FAIL){ if (LOG > 0){ SetConsoleTextAttribute(hConsole, RED); cout << " [ " << cnt << " - FAIL ] " << start << " ---> " << start - 1 << endl; } start--; } else if (result == DEST){ if (LOG > 0){ SetConsoleTextAttribute(hConsole, LRED); cout << " [ " << cnt << " - DEST ] " << start << " ---> " << start << endl; } } } if (LOG > 0){ SetConsoleTextAttribute(hConsole, WHITE); cout << "\n=== [ RESULT ] " << first << " ------> " << last << " (" << cnt << ") " << endl; cout << " PASS " << myresult[PASS] << endl; cout << " NORM " << myresult[NORM] << endl; cout << " FAIL " << myresult[FAIL] << endl; cout << " DEST " << myresult[DEST] << endl; cout << "==================================" << endl; } for (int i = 0; i < 4; i++) { if (cnt >= BADLUCK){ BADLUCK = cnt; R_BAD[i] = myresult[i]; } if (cnt <= GOODLUCK){ GOODLUCK = cnt; R_LUCK[i] = myresult[i]; } R_EVER[i] += myresult[i]; } } SetConsoleTextAttribute(hConsole, LWHITE); cout << "\n\n======= [ FINAL REPORT " << first << " ---> " << last << " with " << TOTAL << " people ]==========" << endl; SetConsoleTextAttribute(hConsole, MAGENTA); cout << "== [ BEST LUCK - "<< GOODLUCK <<" TRY ]"<< endl; cout << " PASS " << R_LUCK[PASS] << endl; cout << " NORM " << R_LUCK[NORM] << endl; cout << " FAIL " << R_LUCK[FAIL] << endl; cout << " DEST " << R_LUCK[DEST] << endl; SetConsoleTextAttribute(hConsole, GREEN); cout << "== [ EVERAGE - " << (double)cntsum / (double)TOTAL << " TRY ]" << endl; cout << " PASS " << (double)R_EVER[PASS] / (double)TOTAL << endl; cout << " NORM " << (double)R_EVER[NORM] / (double)TOTAL << endl; cout << " FAIL " << (double)R_EVER[FAIL] / (double)TOTAL << endl; cout << " DEST " << (double)R_EVER[DEST] / (double)TOTAL << endl; SetConsoleTextAttribute(hConsole, RED); cout << "== [ WORST LUCK - " << BADLUCK << " TRY ]" << endl; cout << " PASS " << R_BAD[PASS] << endl; cout << " NORM " << R_BAD[NORM] << endl; cout << " FAIL " << R_BAD[FAIL] << endl; cout << " DEST " << R_BAD[DEST] << endl; SetConsoleTextAttribute(hConsole, LWHITE); cout << "=============================================================" << endl; cin >> tmp; } 4>
0 개의 댓글:
댓글 쓰기