fork(1) download
  1. // C++11
  2. //
  3. // 最終方針:
  4. // notePlayで一定回数回して期待値を出す
  5. // 回数は maxTime / noteTime に比例させた
  6. // 各スロットで出てきた3文字の文字列は重複をカウントして保存
  7. // あり得る最も短い文字列であると仮定する
  8. // 前後で2つ重複できるものをペアにする
  9. // 上記以外で1つ重複できるものをペアにする
  10. // ペアの作り方は複数あるが、重複部分の文字列は一意になるので、重複を排除してカウントした文字の出現数も一意になる
  11. // 最も短い文字列の文字数が3つのスロットで一致したら、確定とする
  12. // 文字数が足りない分は出現していないと思われるので、適当に埋める
  13. // 基本は元の出現確率 {2, 4, 5, 6, 6, 7, 8} / 38.0
  14. // 1文字だけ足りない場合は、1つ重複させた部分が重なっていないか、重複させれてない部分の間に何かがあると見なす
  15. // 各台の 期待値 * 閾値 < 最も高い台の期待値 なら切り捨て
  16. // 最低限の回数を回せない場合は適当にquickPlay
  17. // 期待値が最も高い台でquickPlayを最後までやる
  18. // 期待値が閾値以下の場合は何もせずに終了
  19. //
  20. // やったこと:
  21. // テスターがなかったので、埋め込んだ
  22. // デバッガも埋め込めたので、今後はたぶん毎回やる
  23. // デバッガは3種類
  24. // DEBUG 1万回のプレイをしてスコアを出す
  25. // 数秒で終わる
  26. // DEBUG2 1回のプレイで詳細を出力
  27. // DEBUG3 パラメータをグリッドサーチしてどれがいいか探す
  28. // 至る所に埋め込まれてる変な定数はこれをやった結果
  29. // パラメータ調整していないしていないのと比べると3%以上は違う感じ
  30. // 実装の追加とパラメータ調整を交互にやってたので、単純な比較はできてないけど
  31. // スコア計算
  32. // 事前に[何もしない, どれかにquickPlayを最大回数やる]の最大値を保存しておく
  33. // Σ min(結果のコイン / 最大値, 1.0)
  34. //
  35. // 感想など:
  36. // 暫定結果は 857466.02 で5位だけど、下がることはありそう
  37. // ローカルでのスコアは8727とかになっているので、最高でもこの値
  38. // デバッガを埋め込んだのが便利で、こういうのをちゃんとやるのがMMの戦いには必須なのだと認識した
  39. // 処理時間がかかってないおらず、pythonチャンスなので書き換えようかとも思ったけど、面倒になったのでやめた
  40. //
  41.  
  42. #define DEBUG
  43. #define _DEBUG2
  44. #define _DEBUG3
  45. #include <algorithm>
  46. #include <cstdlib>
  47. #include <iostream>
  48. #include <vector>
  49. #include <cmath>
  50. #include "bits/stdc++.h"
  51. #include <sys/time.h>
  52. #include <emmintrin.h>
  53. #include <string>
  54. #include <bitset>
  55.  
  56. #ifdef DEBUG3
  57. double t1;
  58. int t2;
  59. #endif
  60.  
  61. using namespace std;
  62.  
  63. inline long long GetTSC() {
  64. long long lo, hi;
  65. asm volatile ("rdtsc": "=a"(lo), "=d"(hi));
  66. return lo + (hi << 32);
  67. }
  68. inline double GetSeconds() {
  69. return GetTSC() / 2.8e9;
  70. }
  71.  
  72. class XRand
  73. {
  74. public:
  75. unsigned int x, y, z, w;
  76. XRand() { init(); }
  77. void init() { x = 314159261; y = 358979323; z = 846264338; w = 327950288; }
  78. unsigned int next() { unsigned int t = x ^ x << 11; x = y; y = z; z = w; return w = w ^ w >> 19 ^ t ^ t >> 8; }
  79. int nextInt(int m) { return (int)(next() % m); }
  80. };
  81.  
  82. const double TO = 29.0;
  83. const string PT = "AABBBBCCCCCDDDDDDEEEEEEFFFFFFFGGGGGGGG";
  84. const int SCORES[] = {1000, 200, 100, 50, 20, 10, 5};
  85. const int PT_SIZE = 38;
  86. const int MIN_COINS = 100;
  87. const int MAX_COINS = 10000;
  88. const int MIN_MAX_TIME = 100;
  89. const int MAX_MAX_TIME = 10000;
  90. const int MIN_NOTE_TIME = 2;
  91. const int MAX_NOTE_TIME = 10;
  92. const int MIN_MACHINES = 3;
  93. const int MAX_MACHINES = 10;
  94. const int MIN_WHEEL_SIZE = 10;
  95. const int MAX_WHEEL_SIZE = 30;
  96. double starttime, gt;
  97. XRand rnd = XRand();
  98.  
  99. #ifdef DEBUG
  100.  
  101.  
  102. struct XorShift {
  103. uint64_t x = 88172645463325252ULL;
  104.  
  105. XorShift() {}
  106.  
  107. void setSeed(uint64_t seed) {
  108. x = seed;
  109. }
  110.  
  111. uint64_t next() {
  112. x ^= x << 13;
  113. x ^= x >> 7;
  114. x ^= x << 17;
  115. return x;
  116. }
  117.  
  118. int nextInt(int n) {
  119. uint64_t upper = 0xFFFFFFFFFFFFFFFFULL / n * n;
  120. uint64_t v = next();
  121. while (v >= upper) {
  122. v = next();
  123. }
  124. return v % n;
  125. }
  126.  
  127. double nextDouble() {
  128. uint64_t v = next() >> 11; // 53bit
  129. return (double)v / (1ULL << 53);
  130. }
  131. };
  132.  
  133. XorShift xs;
  134.  
  135. int p_seed, p_coins, s_coins, p_maxTime, p_noteTime, p_numMachines;
  136. int wheels[MAX_MACHINES][3][MAX_WHEEL_SIZE];
  137. int wheel_sizes[MAX_MACHINES];
  138. int pt = 0;
  139.  
  140. class PlaySlots {
  141. public:
  142.  
  143. static void init(int i){
  144. p_seed = i;
  145. xs.setSeed(p_seed);
  146. s_coins = p_coins = xs.next() % (MAX_COINS - MIN_COINS + 1) + MIN_COINS;
  147. p_maxTime = xs.next() % (MAX_MAX_TIME - MIN_MAX_TIME + 1) + MIN_MAX_TIME;
  148. p_noteTime = xs.next() % (MAX_NOTE_TIME - MIN_NOTE_TIME + 1) + MIN_NOTE_TIME;
  149. p_numMachines = xs.next() % (MAX_MACHINES - MIN_MACHINES + 1) + MIN_MACHINES;
  150. for(int i=0; i<p_numMachines; i++){
  151. int size = xs.next() % (MAX_WHEEL_SIZE - MIN_WHEEL_SIZE + 1) + MIN_WHEEL_SIZE;
  152. wheel_sizes[i] = size;
  153. for(int j=0; j<3; j++){
  154. auto wheel = wheels[i][j];
  155. for(int k=0; k<size; k++){
  156. wheel[k] = PT[xs.next() % PT_SIZE] - 'A';
  157. }
  158. }
  159. }
  160. }
  161.  
  162. static void score_print(){
  163. cout << p_seed << " " << p_coins << endl;
  164. }
  165.  
  166. static void print(){
  167. #ifdef DEBUG2
  168. cerr << "seed: " << p_seed << endl;
  169. cerr << "coins: " << p_coins << endl;
  170. cerr << "maxTime: " << p_maxTime << endl;
  171. cerr << "noteTime: " << p_noteTime << endl;
  172. cerr << "numMachines: " << p_numMachines << endl;
  173. for(int i=0; i<p_numMachines; i++){
  174. cerr << endl;
  175. for(int j=0; j<3; j++){
  176. auto wheel = wheels[i][j];
  177. for(int k=0; k<wheel_sizes[i]; k++){
  178. cerr << char(wheel[k] + 'A');
  179. }
  180. cerr << " " << wheel_sizes[i];
  181. cerr << endl;
  182. }
  183. cerr << setprecision(10) << rate(i) << endl;
  184. }
  185. cerr << endl;
  186. #endif
  187. }
  188.  
  189. static double rate(int n){
  190. auto size = wheel_sizes[n];
  191. double r = 0.0;
  192. for(int i=0; i<7; i++){
  193. double tr = SCORES[i];
  194. for(int j=0; j<3; j++){
  195. auto wheel = wheels[n][j];
  196. int cc = 0;
  197. for(int k=0; k<size; k++){
  198. if(wheel[k] == i) cc++;
  199. }
  200. tr *= 1.0 * cc / size;
  201. }
  202. r += tr;
  203. }
  204. return r;
  205. }
  206.  
  207. static int max_rate_id(){
  208. int mi = -1;
  209. double mr = 0;
  210. for(int i=0; i<p_numMachines; i++){
  211. double tr = rate(i);
  212. if(tr > mr){
  213. mr = tr;
  214. mi = i;
  215. }
  216. }
  217. return mi;
  218. }
  219.  
  220. static int quickPlay(int mi, int n){
  221. pt++;
  222. int r = 0;
  223. auto wheel = wheels[mi];
  224. for(int i=0; i<n; i++){
  225. int c = wheel[0][xs.next() % wheel_sizes[mi]];
  226. int d = wheel[1][xs.next() % wheel_sizes[mi]];
  227. int e = wheel[2][xs.next() % wheel_sizes[mi]];
  228. if(c==d && d==e){
  229. r += SCORES[c];
  230. }
  231. }
  232. p_coins += r - n;
  233. return r;
  234. }
  235.  
  236. static vector<string> notePlay(int mi, int n){
  237. pt++;
  238. int r = 0;
  239. auto wheel = wheels[mi];
  240. auto size = wheel_sizes[mi];
  241. vector<string> res;
  242. char cs[10] = {};
  243. for(int i=0; i<n; i++){
  244. auto c = xs.next();
  245. auto d = xs.next();
  246. auto e = xs.next();
  247. for(int j=0; j<3; j++){
  248. cs[j*3] = wheel[0][(c+j) % size] + 'A';
  249. cs[j*3+1] = wheel[1][(d+j) % size] + 'A';
  250. cs[j*3+2] = wheel[2][(e+j) % size] + 'A';
  251. }
  252. res.emplace_back(cs);
  253. if(cs[3]==cs[4] && cs[4]==cs[5]){
  254. r += SCORES[cs[3] - 'A'];
  255. }
  256. }
  257. p_coins += r - n;
  258. auto it = res.begin();
  259. res.insert(it, to_string(r));
  260. return res;
  261. }
  262. };
  263. #endif
  264.  
  265. class BrokenSlotMachines {
  266. public:
  267. int playSlots(int coins, int maxTime, int noteTime, int numMachines) {
  268. starttime = GetSeconds();
  269. double RS[11] = {};
  270. int RT[11][3] = {};
  271. int RTM[11] = {};
  272. int RC[11][3][8] = {};
  273. char RSS[11][3][31][4] = {};
  274. char RSI[11][3] = {};
  275. bool RE[11] = {};
  276. double CR[7] = {2, 4, 5, 6, 6, 7, 8};
  277. for(int i=0; i<7; i++){
  278. CR[i] /= 38.0;
  279. }
  280.  
  281. int min_n = 4 + maxTime / noteTime / 199;
  282. int max_n = 13 + maxTime / noteTime / 53;
  283.  
  284. int tt = 0;
  285. int mi = -1;
  286. int start_coins = coins;
  287. if(maxTime < noteTime * min_n * numMachines){
  288. for(; tt<maxTime; ){
  289. if(coins < 1) break;
  290. int res = PlaySlots::quickPlay((mi<0 ? tt%numMachines : mi), 1);
  291. tt += 1;
  292. coins += res - 1;
  293. if(res>9){
  294. mi = tt%numMachines;
  295. }
  296. }
  297. return mi;
  298. }
  299. vector<int> cur;
  300. for(int i=0; i<numMachines; i++){
  301. cur.emplace_back(i);
  302. }
  303. int ct = 0;
  304. for(; (tt+cur.size())*noteTime<=maxTime && ct < max_n; ){
  305. ct++;
  306. if(cur.size() == 1){
  307. mi = cur[0];
  308. break;
  309. }else if(cur.size() == 0){
  310. mi = -1;
  311. break;
  312. }
  313. if(coins < cur.size()) break;
  314. for(const auto &ti: cur){
  315. if(RE[ti]) continue;
  316. auto rc = RC[ti];
  317. auto rsi = RSI[ti];
  318. auto resv = PlaySlots::notePlay(ti, 1);
  319. int res = stoi(resv[0]);
  320. int rr = 0;
  321. for(int i=0; i<3; i++){
  322. auto rss = RSS[ti][i];
  323. bool rf = 0;
  324. for(int j=0; j<rsi[i]; j++){
  325. bool rjf = 1;
  326. for(int k=0; k<3; k++){
  327. if(resv[1][k*3+i] != rss[j][k]){
  328. rjf = 0;
  329. break;
  330. }
  331. }
  332. if(rjf){
  333. rf = 1;
  334. rss[j][3]++;
  335. break;
  336. }
  337. }
  338. if(rf) continue;
  339. for(int j=0; j<3; j++){
  340. rss[rsi[i]][j] = resv[1][j*3+i];
  341. }
  342. rss[rsi[i]][3] = 1;
  343. rsi[i]++;
  344. for(int j=0; j<3; j++){
  345. char c = resv[1][i*3+j];
  346. rc[i][c-'A']++;
  347. }
  348. RT[ti][i] += 1;
  349. }
  350. tt++;
  351. coins += res - 1;
  352. }
  353. if(ct >= min_n){
  354. double tm = 0;
  355. for(const auto &ti: cur){
  356. double tmp = 0;
  357. auto rc = RC[ti];
  358. bool RELS[3][31][4] = {};
  359. int KN[3][8] = {};
  360. int KC[3] = {};
  361. for(int i=0; i<3; i++){
  362. auto rss = RSS[ti][i];
  363. auto rsi = RSI[ti][i];
  364. auto rel = RELS[i];
  365. int mm = -1;
  366. int tm = 0;
  367. for(int j=0; j<rsi; j++){
  368. for(int ki=1; ki<rsi; ki++){
  369. int k = (j+ki) % rsi;
  370. if(rel[j][0] || rel[k][1]) continue;
  371. if(rss[j][1] == rss[k][0] && rss[j][2] == rss[k][1]){
  372. rel[j][0] = 1;
  373. rel[k][1] = 1;
  374. tm++;
  375. }
  376. }
  377. }
  378. for(int j=0; j<rsi; j++){
  379. for(int ki=1; ki<rsi; ki++){
  380. int k = (j+ki) % rsi;
  381. if(rel[j][0] || rel[k][1] || rel[j][2] || rel[k][3]) continue;
  382. if(rss[j][2] == rss[k][0]){
  383. rel[j][2] = 1;
  384. rel[k][3] = 1;
  385. }
  386. }
  387. }
  388. for(int j=0; j<8; j++){
  389. rc[i][j] = 0;
  390. }
  391. RT[ti][i] = 0;
  392. for(int j=0; j<rsi; j++){
  393. rc[i][rss[j][0] - 'A']++;
  394. RT[ti][i]++;
  395. if(rel[j][0]) continue;
  396. rc[i][rss[j][1] - 'A']++;
  397. RT[ti][i]++;
  398. if(rel[j][2]) continue;
  399. rc[i][rss[j][2] - 'A']++;
  400. RT[ti][i]++;
  401. }
  402. }
  403. if(RT[ti][0] > 9 && RT[ti][0] == RT[ti][1] && RT[ti][0] == RT[ti][2]){
  404. RE[ti] = 1;
  405. RTM[ti] = RT[ti][0];
  406. }else{
  407. RTM[ti] = max(RT[ti][0], max(RT[ti][1], RT[ti][2]));
  408. }
  409. for(int i=0; i<3; i++){
  410. if(RT[ti][i] != RTM[ti] - 1) continue;
  411. auto rss = RSS[ti][i];
  412. auto rsi = RSI[ti][i];
  413. auto rel = RELS[i];
  414. auto kn = KN[i];
  415. bool kf[7] = {};
  416. for(int j=0; j<rsi; j++){
  417. if(rel[j][2]) kf[rss[j][2] - 'A'] = 1;
  418. if(rel[j][3]) kf[rss[j][0] - 'A'] = 1;
  419. }
  420. for(int j=0; j<rsi; j++){
  421. if(!rel[j][0] && kf[rss[j][2] - 'A']){
  422. kn[rss[j][2] - 'A'] += 1;
  423. for(int k=0; k<rsi; k++){
  424. if(rss[k][0] == rss[j][1] && rss[k][1] == rss[j][2] && rss[k][2] == rss[j][2]){
  425. kn[rss[j][2] - 'A'] += int(rss[k][3]);
  426. }
  427. }
  428. }
  429. if(!rel[j][1] && kf[rss[j][0] - 'A']){
  430. kn[rss[j][0] - 'A'] += 1;
  431. for(int k=0; k<rsi; k++){
  432. if(rss[k][0] == rss[j][0] && rss[k][1] == rss[j][0] && rss[k][2] == rss[j][1]){
  433. kn[rss[j][0] - 'A'] += int(rss[k][3]);
  434. }
  435. }
  436. }
  437. }
  438. for(int j=0; j<7; j++){
  439. kn[j] *= 122;
  440. }
  441. for(int j=0; j<rsi; j++){
  442. if(rel[j][0] || rel[j][2]) continue;
  443. for(int k=0; k<rsi; k++){
  444. if(rel[k][1] || rel[k][3]) continue;
  445. for(int l=0; l<7; l++){
  446. kn[l] += 1;
  447. }
  448. for(int l=0; l<rsi; l++){
  449. if(rss[l][0] == rss[j][2] && rss[l][2] == rss[k][0]){
  450. kn[rss[l][1] - 'A'] += int(rss[l][3]) * 23;
  451. }
  452. }
  453. }
  454. }
  455. }
  456. for(int j=0; j<3; j++){
  457. KC[j] = 0;
  458. for(int k=0; k<7; k++){
  459. KC[j] += KN[j][k];
  460. }
  461. for(int k=0; k<7; k++){
  462. if(1.0 * KN[j][k] / KC[j] > 0.981){
  463. for(int l=0; l<7; l++){
  464. KN[j][k] = l==k;
  465. }
  466. KC[j] = 1;
  467. rc[j][k] += 1;
  468. RT[ti][j] += 1;
  469. break;
  470. }
  471. }
  472. }
  473. for(int i=0; i<7; i++){
  474. double tc = SCORES[i] + 1;
  475. for(int j=0; j<3; j++){
  476. double rts = RTM[ti] - RT[ti][j];
  477. if(KC[j]>0){
  478. tc *= 1.0 * (rc[j][i] + rts * KN[j][i] / KC[j] / 2.34) / (RTM[ti] - rts / 2.34);
  479. }else{
  480. tc *= 1.0 * (rc[j][i] + rts * CR[i] / 1.82) / (RTM[ti] - rts / 1.82);
  481. }
  482. }
  483. tmp += tc;
  484. }
  485. if(tmp > tm){
  486. tm = tmp;
  487. mi = ti;
  488. }
  489. RS[ti] = tmp;
  490. }
  491. if(ct >= min_n){
  492. vector<int> next;
  493. for(const auto &ti: cur){
  494. if(RS[ti] * (RE[mi] ? 1.38 : 2.18) < RS[mi]) continue;
  495. next.emplace_back(ti);
  496. }
  497. cur = next;
  498. }
  499. }
  500. }
  501. tt *= noteTime;
  502. int htt = tt;
  503.  
  504. #ifdef DEBUG2
  505. cerr << "htt: " << htt << " coins: " << coins << endl;
  506. #endif
  507.  
  508. if(mi >= 0 && RS[mi] > 1.13){
  509. for(; tt<maxTime; ){
  510. if(coins < 1) break;
  511. int res = PlaySlots::quickPlay(mi, 1);
  512. tt += 1;
  513. coins += res - 1;
  514. }
  515. }
  516.  
  517. #ifdef DEBUG2
  518. cerr << coins << " mi: " << mi << " htt: " << htt << " tt: " << tt << " time: " << (GetSeconds()-starttime) << endl;
  519. for(int i=0; i<numMachines; i++){
  520. cerr << i << ": " << RS[i] << ", (" << RT[i][0] << ", " << RT[i][1] << ", " << RT[i][2] << ")" << endl;
  521. }
  522. #endif
  523.  
  524. return mi;
  525. }
  526. };
  527.  
  528. #ifdef DEBUG
  529. #ifdef DEBUG2
  530. const int TEST_MAX = 1;
  531. #else
  532. const int TEST_MAX = 10000;
  533. #endif
  534. int main() {
  535. srand((unsigned) time(NULL));
  536. rand();
  537. auto bsm = BrokenSlotMachines();
  538. int max_scores[TEST_MAX+1] = {};
  539. ifstream ifs("./max_score.txt");
  540. int seed, score;
  541. for(int i=1; i<=TEST_MAX; i++){
  542. ifs >> seed >> score;
  543. max_scores[seed] = score;
  544. }
  545.  
  546. double m_rate = 0;
  547. #ifdef DEBUG3
  548. for(t1=1.15; t1<=1.16; t1+=0.01) for(t2=0; t2<=0; t2+=1){
  549. #endif
  550. int hit = 0;
  551. int m1 = 0;
  552. int m2 = 0;
  553. int over = 0;
  554. double os = 0;
  555. double rate_score = 0;
  556. for(int i=1; i<=TEST_MAX; i++){
  557. #ifdef DEBUG2
  558. PlaySlots::init(rand());
  559. #else
  560. PlaySlots::init(i);
  561. #endif
  562. PlaySlots::print();
  563. int mp_coins = p_coins;
  564. int mi = bsm.playSlots(p_coins, p_maxTime, p_noteTime, p_numMachines);
  565. if(mi < 0) m1++;
  566. if(mi == PlaySlots::max_rate_id()) hit++;
  567. #ifdef DEBUG2
  568. PlaySlots::score_print();
  569. }
  570. #else
  571. double score = 1.0 * p_coins / max_scores[i];
  572. rate_score += score;
  573. if(score > 1){
  574. over++;
  575. os += score - 1;
  576. }
  577. if(score < 0.1){
  578. m2++;
  579. }
  580. #ifdef DEBUG3
  581. }
  582. if(m_rate < rate_score - os){
  583. m_rate = rate_score - os;
  584. cerr << t1 << ", " << t2 << ": " << rate_score - os << " os: " << os << " MAX!!" << endl;
  585. }else{
  586. cerr << t1 << ", " << t2 << ": " << rate_score - os << " os: " << os << endl;
  587. }
  588. }
  589. #else
  590. if(i%1000==1){
  591. cerr << i << " " << (rate_score-os) << " hit: " << hit << " m1: " << m1 << " m2: " << m2 << " over: " << over << " os: " << os << endl;
  592. }
  593. }
  594. cerr << "rate_score: " << (rate_score-os) << " hit: " << hit << " m1: " << m1 << " m2: " << m2 << " over: " << over << " os: " << os << endl;
  595. #endif
  596. #endif
  597. return 0;
  598. }
  599. #endif
  600.  
Success #stdin #stdout #stderr 4.04s 4296KB
stdin
Standard input is empty
stdout
Standard output is empty
stderr
1 -nan hit: 1 m1: 0 m2: 0 over: 1 os: inf
1001 -nan hit: 755 m1: 2 m2: 0 over: 978 os: inf
2001 -nan hit: 1513 m1: 2 m2: 0 over: 1959 os: inf
3001 -nan hit: 2281 m1: 2 m2: 0 over: 2945 os: inf
4001 -nan hit: 3073 m1: 3 m2: 0 over: 3933 os: inf
5001 -nan hit: 3848 m1: 4 m2: 0 over: 4918 os: inf
6001 -nan hit: 4630 m1: 4 m2: 0 over: 5905 os: inf
7001 -nan hit: 5382 m1: 5 m2: 0 over: 6887 os: inf
8001 -nan hit: 6151 m1: 5 m2: 0 over: 7874 os: inf
9001 -nan hit: 6932 m1: 5 m2: 0 over: 8859 os: inf
rate_score: -nan hit: 7691 m1: 5 m2: 0 over: 9850 os: inf