player_30a8a177
목록으로공개 전적
0W 0L 0D
도전자 0명이 남긴 결과입니다. 같은 사람이 여러 번 도전하면 마지막 결과 하나만 셉니다.
브라우저에서 계산한 결과라 조작을 완전히 막지 못합니다. 공식 기록으로 쓰지 않습니다.
미보고 도전 0건
출전 코드
/**
* 코드배틀 가위바위보 — 적응형 메타 전략
*
* 핵심 아이디어
* 1) 점수 비대칭: 가위 1점, 바위 2점, 보 3점.
* "내 득점 - 상대 득점"을 효용으로 보면 이 게임의 내시 균형은
* 가위 1/2, 바위 1/6, 보 1/3 (기대값 0). 정보가 없을 때는 이 비율로 섞는다.
* 완전 랜덤 상대에게는 '보'가 턴당 +0.67로 최선이다.
* 2) 여러 예측기(빈도, 마르코프 체인, 승패 반응 등)가 상대의 다음 수 분포를 예측하고,
* 각 예측을 0/1/2칸 회전시킨 변형(상대가 나를 역이용하는 경우 대비)까지 후보로 둔다.
* 3) 모든 후보를 매 턴 가상으로 채점(감쇠 누적)해 가장 잘 맞히는 후보를 따른다.
* 확신이 없으면 내시 혼합으로 후퇴해 착취당하지 않게 한다.
*/
// 내부 표현: 0=가위, 1=바위, 2=보 (반환 시 +1)
var myRecentChoice;
var myH, opH, resH; // 게임 전체 히스토리
var tables; // 예측기별 문맥 → 상대 수 카운트
var scores; // 후보별 가상 점수
var choices; // 이번 턴 후보별 선택
var keys; // 이번 턴 예측기별 문맥 키
var chosen; // 이번 턴 실제로 따른 후보 (-1이면 내시)
var roundNet;
var SCORE_DECAY = 0.95;
var THRESHOLD = 1.5; // 최고 후보 점수가 이보다 낮으면 내시 혼합
function pay(a, o) {
if (a === o) return 0;
if ((a - o + 3) % 3 === 1) return a + 1; // 내가 이김: 낸 수 점수 획득
return -(o + 1); // 내가 짐: 상대가 낸 수 점수만큼 손해
}
function nashMove() {
var r = Math.random();
return r < 0.5 ? 0 : (r < 2 / 3 ? 1 : 2);
}
function bestVs(d) {
var best = -1, bv = -1e9;
for (var a = 0; a < 3; a++) {
var v = 0;
for (var o = 0; o < 3; o++) v += d[o] * pay(a, o);
if (v > bv) { bv = v; best = a; }
}
return best;
}
// 예측기 정의: 문맥 키(없으면 null)와 카운트 감쇠율
var PREDICTORS = [
{ decay: 0.98, key: function (t) { return "f"; } }, // 장기 빈도
{ decay: 0.85, key: function (t) { return "f"; } }, // 단기 빈도
{ decay: 0.92, key: function (t) { return t < 1 ? null : "o" + opH[t - 1]; } }, // 상대 직전 수
{ decay: 0.92, key: function (t) { return t < 1 ? null : "m" + myH[t - 1]; } }, // 내 직전 수에 대한 반응
{ decay: 0.92, key: function (t) { return t < 1 ? null : "p" + myH[t - 1] + opH[t - 1]; } },
{ decay: 0.92, key: function (t) { return t < 1 ? null : "r" + opH[t - 1] + resH[t - 1]; } }, // 승패 반응
{ decay: 0.95, key: function (t) { return t < 2 ? null : "oo" + opH[t - 2] + opH[t - 1]; } },
{ decay: 0.95, key: function (t) { return t < 2 ? null : "pp" + myH[t - 2] + opH[t - 2] + myH[t - 1] + opH[t - 1]; } },
{ decay: 0.97, key: function (t) { return t < 3 ? null : "ppp" + myH[t - 3] + opH[t - 3] + myH[t - 2] + opH[t - 2] + myH[t - 1] + opH[t - 1]; } }
];
var NP = PREDICTORS.length;
var NC = NP * 3 + 3; // 예측기×회전3 + 고정 수 3개
function onGameStart() {
myH = []; opH = []; resH = [];
tables = [];
for (var i = 0; i < NP; i++) tables.push({});
scores = [];
for (var k = 0; k < NC; k++) scores.push(0);
choices = []; keys = [];
printLog("onGameStart!");
}
function onRoundStart() {
roundNet = 0;
// 라운드마다 상대가 초기화될 수 있으므로 점수 신뢰도를 절반으로
for (var k = 0; k < NC; k++) scores[k] *= 0.5;
}
function onTurnStart() {
var t = opH.length;
choices = []; keys = [];
for (var i = 0; i < NP; i++) {
var key = PREDICTORS[i].key(t);
keys.push(key);
var d = key === null ? null : tables[i][key];
for (var s = 0; s < 3; s++) {
if (!d) { choices.push(-1); continue; }
var ds = [0, 0, 0];
for (var m = 0; m < 3; m++) ds[(m + s) % 3] = d[m];
choices.push(bestVs(ds));
}
}
choices.push(0, 1, 2); // 고정 수 후보
chosen = -1;
var bv = THRESHOLD;
for (var k = 0; k < NC; k++) {
if (choices[k] >= 0 && scores[k] > bv) { bv = scores[k]; chosen = k; }
}
var move = chosen >= 0 ? choices[chosen] : nashMove();
myRecentChoice = move + 1;
return myRecentChoice;
}
function onTurnEnd(result) {
var o = result.opponentchoice - 1;
var a = myRecentChoice - 1;
if (o < 0 || o > 2) o = 0; // 방어적 처리
for (var k = 0; k < NC; k++) {
scores[k] = scores[k] * SCORE_DECAY + (choices[k] >= 0 ? pay(choices[k], o) : 0);
}
for (var i = 0; i < NP; i++) {
var key = keys[i];
if (key === null) continue;
var c = tables[i][key];
if (!c) { c = [0, 0, 0]; tables[i][key] = c; }
var dec = PREDICTORS[i].decay;
c[0] *= dec; c[1] *= dec; c[2] *= dec;
c[o] += 1;
}
var p = pay(a, o);
roundNet += p;
myH.push(a); opH.push(o); resH.push(p > 0 ? 1 : (p < 0 ? 2 : 0));
}
function onRoundEnd(result) {
printLog("round " + (result.win === 1 ? "WIN" : result.win === 0 ? "DRAW" : "LOSE") +
" (net " + roundNet + ") — " + result.wincnt + "승 " + result.losecnt + "패");
}
function onGameEnd(result) {
printLog("game " + (result.win === 1 ? "WIN" : result.win === 0 ? "DRAW" : "LOSE"));
printLog("----------------------------");
}
도전 기록
아직 도전받은 적이 없습니다.
기록된 수를 되짚습니다. 코드를 다시 실행하지 않습니다. 각 라운드의 앞 20턴만 보여 줍니다.