Created
May 23, 2019 14:37
-
-
Save hakomo/f560b344986fff6c8ee0d87ff66d32ab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if 1 | |
#pragma GCC optimize "O3" | |
#include <array> | |
#include <bitset> | |
#include <deque> | |
#include <map> | |
#include <queue> | |
#include <set> | |
#include <unordered_set> | |
#include <unordered_map> | |
#include <vector> | |
#include <algorithm> | |
#include <functional> | |
#include <string> | |
#include <fstream> | |
#include <iostream> | |
#include <sstream> | |
#include <random> | |
#include <cassert> | |
#include <cmath> | |
#include <cstring> | |
#include <chrono> | |
using namespace std; | |
using i8 = int8_t; using i16 = int16_t; using i32 = int32_t; using i64 = int64_t; using u8 = uint8_t; using u16 = uint16_t; using u32 = uint32_t; using u64 = uint64_t; using f32 = float; using f64 = double; | |
template <class T> inline T sq(T a) { return a * a; } | |
template <class T> inline bool chmin(T& a, const T& b) { if (b < a) { a = b; return true; } return false; } | |
template <class T> inline bool chmax(T& a, const T& b) { if (a < b) { a = b; return true; } return false; } | |
void _u() { cerr << endl; } template <class H, class... T> void _u(H&& h, T&&... t) { cerr << h << ", "; _u(move(t)...); } | |
#define _polyfill(v) v | |
#define _overload3(a, b, c, d, ...) d | |
#define _rep3(i, a, b) for (i32 i = i32(a); i < i32(b); ++i) | |
#define _rep2(i, b) _rep3(i, 0, b) | |
#define _rep1(i) for (i32 i = 0; ; ++i) | |
#define rep(...) _polyfill(_overload3(__VA_ARGS__, _rep3, _rep2, _rep1)(__VA_ARGS__)) | |
#define A(a) begin(a), end(a) | |
#define S(a) i32((a).size()) | |
#define U(...) { cerr << #__VA_ARGS__ << ": "; _u(__VA_ARGS__); } | |
#define lin U(__LINE__) | |
#define exi exit(0); | |
#endif | |
constexpr bool Deterministic = 0; | |
class rando { | |
unsigned y; | |
public: | |
rando(unsigned y) : y(y) {} | |
unsigned next() { | |
return y ^= (y ^= (y ^= y << 13) >> 17) << 5; | |
} | |
int next(int b) { | |
return next() % b; | |
} | |
int next(int a, int b) { | |
return next(b - a) + a; | |
} | |
double nextDouble(double b = 1) { | |
return b * next() / 4294967296.0; | |
} | |
double nextDouble(double a, double b) { | |
return nextDouble(b - a) + a; | |
} | |
int operator() (int b) { | |
return next(b); | |
} | |
} rando(Deterministic ? 2463534242 : random_device()()); | |
i32 W, H, discard; | |
f64 predictedPerPos[100][100]; | |
map<vector<i32>, array<f64, 10>> as; | |
f64 kk[10][4][102]; | |
pair<i32, f64> yp[10][4][102]; | |
i32 id; | |
pair<array<f64, 10>, i32> ll[102][5]; | |
i32 backupRealYs[102]; | |
struct { | |
f64 normalizer; | |
i32 score[100][100][3][256]; | |
f64 eval(i32 x, i32 y, u8 c[]) const { | |
return sq(1 - (score[y][x][0][c[0]] + score[y][x][1][c[1]] + score[y][x][2][c[2]]) * normalizer); | |
} | |
} evaluator; | |
struct Mino { | |
i32 i, r, n, m, w, h, x[4], y[4], top[4], bot[4]; | |
bool contains(i32 a, i32 b) const { | |
rep (i, n) { | |
if (x[i] == a && y[i] == b) return true; | |
} | |
return false; | |
} | |
} minos[10][4]; | |
struct { | |
i32 w, h, s; | |
u8 c[800][800][3]; | |
void colorize(const Mino& m, u8 a[][3]) const { | |
i32 x0 = rando.next(w); | |
i32 y0 = rando.next(h); | |
rep (i, m.n) { | |
i32 x1 = rando.next(max(0, x0 - s), min(w, x0 + s + 1)); | |
i32 y1 = rando.next(max(0, y0 - s), min(h, y0 + s + 1)); | |
memcpy(a[i], c[y1][x1], sizeof(a[0])); | |
} | |
} | |
} colorizer; | |
struct powt { | |
f64 _[4], a[12]; | |
powt() { | |
rep (i, -4, 12) { | |
a[i] = pow(0.5, i); | |
} | |
} | |
} powt; | |
struct Board { | |
i32 r, x; | |
f64 score0, score1; | |
f64 score, predicted; | |
f64 scoresPerMino[10]; | |
struct { | |
i32 r, v; | |
} ys[104]; | |
i32 calcVirtualY(i32 x) const { | |
return ys[x - 1].r | (ys[x].r - 1) | ys[x + 1].r ? min(ys[x].r, max(ys[x - 1].r, ys[x + 1].r) + 2) : 0; | |
} | |
void drop(const Mino& m, u8 colors[][3], i32 x, i32 y) { | |
this->r = m.r; | |
this->x = x; | |
rep (i, m.n) { | |
score += evaluator.eval(x + m.x[i] - 2, y + m.y[i], colors[i]); | |
} | |
predicted = yp[m.i][m.r & (m.m - 1)][x].second; | |
rep (i, m.w) { | |
ys[x + i].r = y + m.top[i]; | |
} | |
rep (i, -1, m.w + 1) { | |
ys[x + i].v = calcVirtualY(x + i); | |
} | |
} | |
void updateScores0(i32 bx, i32 ex, f64 scores[]) const { | |
memset(scores, 0, sizeof(f64) * 10); | |
for (auto& ms : minos) { | |
rep (ri, ms[0].m) { | |
auto& m = ms[ri]; | |
rep (x, max(2, bx - m.w - 1), min(W - m.w + 3, ex + 2)) { | |
scores[m.i] += kk[m.i][ri][x]; | |
} | |
} | |
} | |
} | |
void g() { | |
for (auto& ms : minos) { | |
i32 c = 4 / ms[0].m; | |
rep (ri, ms[0].m) { | |
auto& m = ms[ri]; | |
rep (x, 2, W - m.w + 3) { | |
i32 y = ys[x].r - m.bot[0]; | |
rep (i, 1, m.w) { | |
chmin(y, ys[x + i].r - m.bot[i]); | |
} | |
if (y <= 0) { | |
yp[m.i][m.r][x].first = -1; | |
kk[m.i][m.r][x] = 0; | |
continue; | |
} | |
--y; | |
rep (i, m.w) { | |
ys[x + i].r = y + m.top[i]; | |
} | |
f64 p = predicted; | |
i32 sm = 0; | |
for (i32 i : { -1, m.w }) { | |
i32 by = calcVirtualY(x + i); | |
sm += ys[x + i].v - by; | |
rep (y, by, ys[x + i].v) { | |
p += predictedPerPos[y][x - 2]; | |
} | |
} | |
rep (i, m.w) { | |
i32 by = calcVirtualY(x + i); | |
sm += ys[x + i].v - by - m.bot[i] - 1; | |
rep (y, by, ys[x + i].v) { | |
p += predictedPerPos[y][x - 2]; | |
} | |
} | |
yp[m.i][m.r][x] = { y, p }; | |
kk[m.i][m.r][x] = sm < 12 ? powt.a[sm] * c : 0; | |
rep (i, m.w) { | |
ys[x + i].r = backupRealYs[x + i]; | |
} | |
} | |
} | |
} | |
} | |
void updateScores1(i32 bx, i32 ex, f64 scores[]) { | |
rep (x, max(2, bx - 4 - 1), min(W - 1 + 3, ex + 5)) { | |
backupRealYs[x] = ys[x].r; | |
} | |
for (auto& ms : minos) { | |
i32 c = 4 / ms[0].m; | |
rep (ri, ms[0].m) { | |
auto& m = ms[ri]; | |
rep (x, max(2, bx - m.w - 1), min(W - m.w + 3, ex + 2)) { | |
i32 y = ys[x].r - m.bot[0]; | |
rep (i, 1, m.w) { | |
chmin(y, ys[x + i].r - m.bot[i]); | |
} | |
if (y <= 0) continue; | |
--y; | |
rep (i, m.w) { | |
ys[x + i].r = y + m.top[i]; | |
} | |
i32 sm = 0; | |
for (i32 i : { -1, m.w }) { | |
sm += ys[x + i].v - calcVirtualY(x + i); | |
} | |
rep (i, m.w) { | |
sm += ys[x + i].v - calcVirtualY(x + i) - m.bot[i] - 1; | |
} | |
if (sm < 12) { | |
scores[m.i] += powt.a[sm] * c; | |
} | |
rep (i, m.w) { | |
ys[x + i].r = backupRealYs[x + i]; | |
} | |
} | |
} | |
} | |
} | |
void dropAndUpdateScores(const Mino& m, u8 colors[][3], i32 x, i32 y) { | |
if (ll[x][m.w].second != id) { | |
ll[x][m.w].second = id; | |
updateScores0(x, x + m.w, ll[x][m.w].first.data()); | |
} | |
rep (i, 10) { | |
scoresPerMino[i] -= ll[x][m.w].first[i]; | |
} | |
drop(m, colors, x, y); | |
static vector<i32> a; | |
a = { x }; | |
rep (i, m.w) { | |
a.emplace_back(ys[x + i].r); | |
} | |
if (!as.count(a)) { | |
updateScores1(x, x + m.w, as[a].data()); | |
} | |
{ | |
auto& s = as[a]; | |
rep (i, 10) { | |
scoresPerMino[i] += s[i]; | |
} | |
} | |
static f64 c[]{ | |
1.0 / (W * 2 + (W - 1) * 2), | |
1.0 / (W * 2 + (W - 2) * 2), | |
1.0 / ((W - 1) * 4), | |
1.0 / (W * 2 + (W - 3) * 2), | |
1.0 / ((W - 1) * 4), | |
1.0 / ((W - 2) * 2 + (W - 1) * 2), | |
1.0 / ((W - 2) * 2 + (W - 1) * 2), | |
1.0 / ((W - 2) * 2 + (W - 1) * 2), | |
1.0 / ((W - 2) * 2 + (W - 1) * 2), | |
1.0 / ((W - 2) * 2 + (W - 1) * 2), | |
}; | |
f64 scores[10]; | |
rep (i, 10) { | |
scores[i] = scoresPerMino[i] * c[i]; | |
} | |
sort(A(scores)); | |
score1 = 0; | |
rep (i, 10) { | |
score1 += scores[i] * powt.a[i]; | |
} | |
score0 = score / predicted; | |
} | |
}; | |
Board rootbo; | |
vector<Board> nextbos; | |
struct ImageFromBlocks { | |
string init(i32 imageH, i32 blockS, i32 maxDiscard, const vector<i32>& image) const { | |
i32 imageW = S(image) / imageH; | |
W = imageW / blockS; | |
H = imageH / blockS; | |
discard = maxDiscard; | |
colorizer.w = imageW; | |
colorizer.h = imageH; | |
colorizer.s = blockS; | |
rep (y, colorizer.h) { | |
rep (x, colorizer.w) { | |
rep (t, 3) { | |
colorizer.c[y][x][t] = image[y * colorizer.w + x] >> t * 8 & 255; | |
} | |
} | |
} | |
{ | |
evaluator.normalizer = 1.0 / 765 / sq(blockS); | |
i32 a[257], b[257]; | |
rep (y0, H) { | |
rep (x0, W) { | |
rep (t, 3) { | |
fill(A(a), 0); | |
fill(A(b), 0); | |
rep (y1, y0 * blockS, (y0 + 1) * blockS) { | |
rep (x1, x0 * blockS, (x0 + 1) * blockS) { | |
i32 c = image[y1 * imageW + x1] >> t * 8 & 255; | |
a[c + 1] += c; | |
++b[c + 1]; | |
} | |
} | |
rep (c, 256) { | |
a[c + 1] += a[c]; | |
b[c + 1] += b[c]; | |
} | |
rep (c, 256) { | |
evaluator.score[y0][x0][t][c] = c * (b[c] - b[0]) - (a[c] - a[0]) + (a[256] - a[c]) - c * (b[256] - b[c]); | |
} | |
} | |
} | |
} | |
} | |
{ | |
unordered_map<i32, i32> mp; | |
for (i32 c : image) { | |
++mp[c]; | |
} | |
vector<pair<i32, i32>> a(A(mp)); | |
i32 s = min(S(a), 50000000 / (W * H)); | |
random_shuffle(A(a), rando); | |
f64 sm = 0; | |
rep (i, s) { | |
sm += a[i].second; | |
} | |
sm = 1 / sm; | |
rep (y, H) { | |
rep (x, W) { | |
predictedPerPos[y][x] = 0; | |
f64 s2 = 0; | |
rep (i, s) { | |
u8 c[3]; | |
rep (t, 3) { | |
c[t] = a[i].first >> (t << 3) & 255; | |
} | |
f64 sc = evaluator.eval(x, y, c); | |
predictedPerPos[y][x] += sc * a[i].second; | |
s2 += sq(sc) * a[i].second; | |
} | |
predictedPerPos[y][x] *= sm; | |
predictedPerPos[y][x] += 0.5 - 0.1 * y / (H - 1) + sqrt(s2 * sm - sq(predictedPerPos[y][x])) * 2; | |
} | |
} | |
} | |
{ | |
vector<vector<vector<i32>>> a{ | |
{{0,1}}, | |
{{0,1,2}}, | |
{{0,-1},{1,2}}, | |
{{0,1,2,3}}, | |
{{0,1},{2,3}}, | |
{{-1,0,-1},{1,2,3}}, | |
{{0,1,-1},{-1,2,3}}, | |
{{-1,0,1},{2,3,-1}}, | |
{{0,-1,-1},{1,2,3}}, | |
{{-1,-1,0},{1,2,3}} | |
}; | |
rep (i, S(a)) { | |
auto& b = a[i]; | |
i32 n = 0; | |
rep (y, S(b)) { | |
rep (x, S(b[0])) { | |
n += b[y][x] != -1; | |
} | |
} | |
rep (r, 4) { | |
auto& m = minos[i][r]; | |
m.i = i; | |
m.r = r; | |
m.n = n; | |
m.w = S(b[0]); | |
m.h = S(b); | |
rep (x, m.w) { | |
i32 y = 0; | |
for (; b[y][x] == -1; ++y); | |
m.top[x] = y; | |
y = m.h - 1; | |
for (; b[y][x] == -1; --y); | |
m.bot[x] = y; | |
} | |
rep (y, m.h) { | |
rep (x, m.w) { | |
if (b[y][x] != -1) { | |
m.x[b[y][x]] = x; | |
m.y[b[y][x]] = y; | |
} | |
} | |
} | |
vector<vector<i32>> c(m.w, vector<i32>(m.h)); | |
rep (y, m.h) { | |
rep (x, m.w) { | |
c[x][m.h - 1 - y] = b[y][x]; | |
} | |
} | |
b = c; | |
} | |
} | |
rep (ri, 4) { | |
minos[0][ri].m = 2; | |
minos[1][ri].m = 2; | |
minos[2][ri].m = 4; | |
minos[3][ri].m = 2; | |
minos[4][ri].m = 1; | |
minos[5][ri].m = 4; | |
minos[6][ri].m = 2; | |
minos[7][ri].m = 2; | |
minos[8][ri].m = 4; | |
minos[9][ri].m = 4; | |
} | |
} | |
rootbo.score = rootbo.predicted = 0; | |
rep (x, W + 4) { | |
rootbo.ys[x].r = rootbo.ys[x].v = 2 <= x && x < W + 2 ? H : 0; | |
} | |
rootbo.updateScores1(2, W + 2, rootbo.scoresPerMino); | |
return ""; | |
} | |
string placePiece(i32 h, const vector<i32>& piece, i32 _) const { | |
i32 w = S(piece) / h; | |
i32 mi = 0; | |
for (; ; ++mi) { | |
auto& m = minos[mi][0]; | |
bool ok = true; | |
rep (y, h) { | |
rep (x, w) { | |
if (piece[y * w + x] == -1) { | |
if (m.contains(x, y)) { | |
ok = false; | |
break; | |
} | |
} else { | |
if (!m.contains(x, y)) { | |
ok = false; | |
break; | |
} | |
} | |
} | |
if (!ok) break; | |
} | |
if (ok) break; | |
} | |
auto& ms = minos[mi]; | |
u8 colors[4][3]; | |
{ | |
auto& m = ms[0]; | |
rep (i, m.n) { | |
i32 c = piece[m.y[i] * m.w + m.x[i]]; | |
rep (t, 3) { | |
colors[i][t] = c >> t * 8 & 255; | |
} | |
} | |
} | |
rep (x, 2, W + 2) { | |
backupRealYs[x] = rootbo.ys[x].r; | |
} | |
rootbo.g(); | |
struct Neighbor { | |
i32 r, x, y; | |
Neighbor() {} | |
Neighbor(i32 r, i32 x, i32 y) : r(r), x(x), y(y) {} | |
}; | |
f64 mn0 = 1 << 30, mx0 = -1 << 30; | |
static vector<Neighbor> neighbors; | |
neighbors.clear(); | |
for (auto& m : ms) { | |
rep (x, 2, W - m.w + 3) { | |
auto& p = yp[m.i][m.r & (m.m - 1)][x]; | |
i32 y = p.first; | |
f64 predicted = p.second; | |
if (y == -1) continue; | |
neighbors.emplace_back(m.r, x, y); | |
f64 score = rootbo.score; | |
rep (i, m.n) { | |
score += evaluator.eval(x + m.x[i] - 2, y + m.y[i], colors[i]); | |
} | |
chmin(mn0, score / predicted); | |
chmax(mx0, score / predicted); | |
} | |
} | |
if (discard) { | |
constexpr i32 n = 50; | |
f64 sm = 0.5; | |
f64 rm = 0; | |
rep (x, 2, W + 2) { | |
rm += rootbo.ys[x].v; | |
} | |
rm = discard / (discard + rm / 3.6) / 1.25 * (n + 1); | |
rep (_, n) { | |
auto& ms = minos[rando.next(10)]; | |
f64 mx = 0; | |
u8 colors[4][3]; | |
colorizer.colorize(ms[0], colors); | |
for (auto& m : ms) { | |
rep (x, 2, W - m.w + 3) { | |
auto& p = yp[m.i][m.r & (m.m - 1)][x]; | |
i32 y = p.first; | |
f64 predicted = p.second; | |
if (y == -1 || mx * predicted > rootbo.score + m.n) continue; | |
f64 score = rootbo.score; | |
rep (i, m.n) { | |
score += evaluator.eval(x + m.x[i] - 2, y + m.y[i], colors[i]); | |
} | |
if (mx * predicted < score) { | |
mx = score / predicted; | |
if (mx > mx0) goto END; | |
} | |
} | |
} | |
END: | |
if (mx < mx0) { | |
sm += 1; | |
} else if (mx == mx0) { | |
sm += 0.5; | |
} | |
if (rm < sm) break; | |
} | |
if (rm > sm) { | |
--discard; | |
return "d"; | |
} | |
} | |
nextbos.clear(); | |
++id; | |
as.clear(); | |
f64 mn1 = 1 << 30, mx1 = -1 << 30; | |
for (auto& n : neighbors) { | |
nextbos.emplace_back(rootbo); | |
auto& bo = nextbos.back(); | |
bo.dropAndUpdateScores(ms[n.r], colors, n.x, n.y); | |
chmin(mn1, bo.score1); | |
chmax(mx1, bo.score1); | |
} | |
if (S(nextbos) == 0) { | |
return "0 0"; | |
} else if (S(nextbos) == 1) { | |
auto& bo = nextbos[0]; | |
rootbo = bo; | |
return to_string(bo.r) + ' ' + to_string(bo.x - 2); | |
} | |
auto& bo = *max_element(A(nextbos), [&](const Board& a, const Board& b) { | |
constexpr f64 co = 0.013; | |
return (a.score0 - mn0) / (mx0 - mn0) + (a.score1 - mn1) / (mx1 - mn1) * co | |
< (b.score0 - mn0) / (mx0 - mn0) + (b.score1 - mn1) / (mx1 - mn1) * co; | |
}); | |
rootbo = bo; | |
return to_string(bo.r) + ' ' + to_string(bo.x - 2); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment