[雜項/無聊向]《美食大戰老鼠》強卡最優策略搜索代碼(非玩家勿入)

最近常登《美食大戰老鼠》這款沙雕遊戲,不得不讚嘆道裏面的卡片強化系統真的****。爲了最大化用有限卡強化出高星卡的成功概率,我就瞎寫了這麼個大暴力程序。什麼?!你不懂什麼叫用最優策略最大化最終成功的概率?!怪我咯?……

算法方面,哪有什麼算法,直接上個無聊的大 dp 就完事兒了,狀態通過哈希來存。

用下面的代碼實際操作了幾次後感覺最優策略似乎有規律可循,所以應該還有提速空間。代碼本身也有一些可優化的地方,不過懶得改了。

基本使用方式:先輸入 $n$ 表示你已有的卡片數量,再輸入 $n$ 個數表示 $n$ 張卡片的星級(順序可任意),最後輸入一個數表示你希望強化出的卡片星級。每一步程序都會輸出最終成功概率以及當前的最優決策,按要求執行反饋即可。

注意事項:

  1. 此代碼未對任何非法操作做判斷,所以請儘量保證輸入合法(比如,不要出現希望強化出的卡片星級低於已有卡片的情況)
  2. 由於算法過於暴力,所以卡片數量不宜太多(最好不超過 $50$ 張)
  3. 由於未找到 15 星上 16 星強化對應的概率,因此下面的代碼只適用於 15 級以下的強化~~(我並不知道強化的基本概率的計算公式,所以只能打表)~~
  4. 下面的概率表默認強化用卡爲高耗能卡(你應該明白是什麼意思)
  5. 強化默認所使用的是同一種四葉草(對應的程序語句爲const double mul = 1.0;mul表示四葉草對應的概率倍數,這裏的1.0爲不使用四葉草的情況,使用 1 級四葉草則將1.0改爲1.2,以此類推)
  6. 可能有bug

暫時留坑,以後這程序的功能會更加完善,大體準備再修改以下內容:

  • <input type="checkbox" checked>加入同時用多張卡強化一張卡的操作</input>
  • <input type="checkbox">加入 VIP 等級以及公會合成屋的強化概率加成</input>
  • <input type="checkbox">更改輸入格式(目前一個星級一個星級地輸入的方式在數量多時會顯得很麻煩)</input>
  • <input type="checkbox">可選擇查看當前各星級卡的剩餘數量(以驗證程序執行是否與當前實際情況同步,純黑箱操作有時候會忘了當前操作是否執行)</input>
  • <input type="checkbox">進行一些細節的優化</input>

下面的內容是不會更改的~~,這輩子都不會更改的~~:

  1. 允許使用不同的四葉草(如果加入不同種類的四葉草的話,狀態數量會大大增加,程序的處理能力上限也會大大降低)
  2. 可判斷非法操作(謹慎就好~~,這個功能是絕對不會有的~~)

以後有時間再更。

#include<bits/stdc++.h>

using namespace std;

const int N = 1000000;
const int M = 16;
const int mod = 998244353;
const double mul = 1.0;
const double eps = 1e-8;

int n, all, goal;
double p[M][M], f[N];
bool visit[N];
map<int, int> hash_t;
pair<int, vector<int>> best[N];

void init() {
  p[0][0] = 1;
  p[1][1] = 1;
  p[2][2] = 0.9683;
  p[3][3] = 0.6858;
  p[4][4] = 0.495;
  p[5][5] = 0.3958;
  p[6][6] = 0.3192;
  p[7][7] = 0.2642;
  p[8][8] = 0.22;
  p[9][9] = 0.135;
  p[10][10] = 0.125;
  p[11][11] = 0.116;
  p[12][12] = 0.107;
  p[13][13] = 0.101;
  p[14][14] = 0.095;

  p[1][0] = 0.88;
  p[2][1] = 0.792;
  p[3][2] = 0.55;
  p[4][3] = 0.4033;
  p[5][4] = 0.33;
  p[6][5] = 0.264;
  p[7][6] = 0.212;
  p[8][7] = 0.132;
  p[9][8] = 0.045;
  p[10][9] = 0.044;
  p[11][10] = 0.043;
  p[12][11] = 0.0398;
  p[13][12] = 0.0367;
  p[14][13] = 0.0336;

  p[2][0] = 0.6083;
  p[3][1] = 0.4292;
  p[4][2] = 0.2417;
  p[5][3] = 0.2008;
  p[6][4] = 0.132;
  p[7][5] = 0.106;
  p[8][6] = 0.06;
  p[9][7] = 0.022;
  p[10][8] = 0.018;
  p[11][9] = 0.017;
  p[12][10] = 0.0156;
  p[13][11] = 0.0141;
  p[14][12] = 0.0126;
}

// p = p1 + p2/3 + p3/3
double get(int i, vector<int> j) {
  reverse(j.begin(), j.end());
  double result = 0;
  result += p[i][j[0]];
  if (j.size() > 1) {
    result += p[i][j[1]] / 3;
  }
  if (j.size() > 2) {
    result += p[i][j[2]] / 3;
  }
  return min(result * mul, 1.);
}

pair<bool, int> encode(vector<int> info) {
  bool exist = true;
  int base = 0;
  for (auto x : info) {
    base = (base * 233ll + x) % mod;
  }
  if (!hash_t.count(base)) {
    exist = false;
    hash_t[base] = ++all;
    if (info[goal]) {
      visit[all] = true;
      f[all] = 1;
    }
  }
  return make_pair(exist, hash_t[base]);
}

void strengthen(int i, vector<int> j, vector<int>& number, bool success = true) {
  --number[i];
  for (auto x : j) {
    --number[x];
  }
  if (success) {
    ++number[i + 1];
  } else {
    ++number[i - (i > 5)];
  }
}

int count(vector<int> number) {
  int result = 0;
  for (auto x : number) {
    result += x;
  }
  return result;
}

double dp(vector<int> number) {
  int id = encode(number).second;
  if (visit[id]) {
    return f[id];
  }
  visit[id] = true;
  if (count(number) == 1) {
    return f[id] = 0;
  } else {
    double& answer = f[id]; 
    vector<int> here = number;
    for (int i = 0; i < 15; ++i) {
      if (!number[i]) {
        continue;
      }
      --number[i];
      // 1->x
      for (int j = max(0, i - 2); j <= i; ++j) {
        if (number[j]) {
          vector<int> array1 = here, array0 = here, t1(1);
          t1[0] = j;
          strengthen(i, t1, array1);
          strengthen(i, t1, array0, false);
          double prob = get(i, t1);
          double foo = dp(array1) * prob + dp(array0) * (1 - prob);
          if (foo > answer) {
            answer = foo;
            best[id] = make_pair(i, t1);
          }
        }
      }
      // 2->x
      for (int j = max(0, i - 2); j <= i; ++j) {
        if (number[j]) {
          --number[j];
          for (int k = j; k <= i; ++k) {
            if (number[k]) {
              vector<int> array1 = here, array0 = here, t2(2);
              t2[0] = j;
              t2[1] = k;
              strengthen(i, t2, array1);
              strengthen(i, t2, array0, false);
              double prob = get(i, t2);
              double foo = dp(array1) * prob + dp(array0) * (1 - prob);
              if (foo > answer) {
                answer = foo;
                best[id] = make_pair(i, t2);
              }
            }
          }
          ++number[j];
        }
      }
      // 3->x
      for (int j = max(0, i - 2); j <= i; ++j) {
        if (number[j]) {
          --number[j];
          for (int k = j; k <= i; ++k) {
            if (number[k]) {
              --number[k];
              for (int l = k; l <= i; ++l) {
                if (number[l]) {
                  vector<int> array1 = here, array0 = here, t3(3);
                  t3[0] = j;
                  t3[1] = k;
                  t3[2] = l;
                  strengthen(i, t3, array1);
                  strengthen(i, t3, array0, false);
                  double prob = get(i, t3);
                  double foo = dp(array1) * prob + dp(array0) * (1 - prob);
                  if (foo > answer) {
                    answer = foo;
                    best[id] = make_pair(i, t3);
                  }
                }
              }
              ++number[k]; 
            }
          }
          ++number[j];
        }
      }
      ++number[i];
    }
    return answer;
  }
}

int main() {
  init();
  cin >> n;
  vector<int> number(16);
  for (int i = 1; i <= n; ++i) {
    int x;
    cin >> x;
    ++number[x];
  }
  cin >> goal;
  //freopen("log.txt", "w", stdout);
  cout << "probability: " << setprecision(10) << dp(number) << '\n';
  //assert(all < N);
  //cerr << all << '\n';
  if (dp(number) > eps) {
    while (1) {
      int id = encode(number).second;
      cout << "the best choice is: strengthen " << best[id].first << " with {";
      vector<int>& t = best[id].second;
      for (int i = 0; i < t.size(); ++i) {
        if (i) {
          cout << ", ";
        }
        cout << t[i];
      }
      cout << "}." << '\n';
      cout << "have you succeeded? (input 1 for success and 0 for failure)" << '\n';
      string inf;
      cin >> inf;
      strengthen(best[id].first, best[id].second, number, inf == "1");
      id = encode(number).second;
      if (f[id] == 1) {
        cout << "congratulation!" << '\n';
        break;
      } else if (f[id] < eps) {
        cout << "sorry, it's impossible now." << '\n';
        break; 
      } else {
        cout << "probability now: " << setprecision(10) << f[id] << '\n';
      }
    }
  } else {
    cout << "it's impossible." << '\n';
  }
  cout << "done." << '\n';
  return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章