코드 아레나
코드배틀
In English, please.

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("----------------------------");
}

도전 기록

아직 도전받은 적이 없습니다.