diff --git a/img/circle.png b/img/circle.png new file mode 100644 index 0000000..cc45f9d Binary files /dev/null and b/img/circle.png differ diff --git a/img/cross.png b/img/cross.png new file mode 100644 index 0000000..ad077d7 Binary files /dev/null and b/img/cross.png differ diff --git a/normal.cpp b/normal.cpp new file mode 100644 index 0000000..484a19b --- /dev/null +++ b/normal.cpp @@ -0,0 +1,94 @@ +#include +#include + +enum tile {fr, p1, p2}; + +struct pos { + int y, x; + pos(int py, int px) : y(py), x(px) {} + pos() {} + pos operator*(int n) { + return {y*n, x*n}; + } + pos operator+(pos p){ + return {p.y+y, p.x+x}; + } + void operator+=(pos p){ + y += p.y; + x += p.x; + } +}; + +struct Game { + tile won = fr; + int size; + std::vector> map; + Game(int s) : size(s){ + std::vector row(size, fr); + map = std::vector>(size, row); + } + tile at(pos p) { + return map[p.y][p.x]; + } + bool valid(pos p) { + return p.x >= 0 && p.x < size && p.y >= 0 && p.y < size; + } + int place(pos p, tile t) { + if(!valid(p)) return 1; + if(at(p) != fr) return 2; + map[p.y][p.x] = t; + return 0; + } + bool checkWin(pos p) { + int win_count = size < 5 ? size : 5; + tile t0 = at(p); + if(t0 == fr) return false; + for(int vy = -1; vy <= 1; vy++) { + for(int vx = -1; vx <= 1; vx++) { + if(vy == 0 && vx == 0) continue;; + pos v = {vy,vx}; + bool win = true; + for(int n = 1; n < 5; n++) { + if(!valid(v*n+p) || at(v*n+p) != t0) { + win = false; + break; + } + } + if(win) { + won = t0; + return true; + } + } + } + return false; + } + void print() { + for(auto r : map) { + for(tile t : r) { + std::cout << ((t == fr) ? '.' : (t == p1) ? 'O' : 'X'); + } + std::cout << std::endl; + } + } +}; + +int main(int argc, char** argv) { + Game g(15); + pos p(0,0); + std::cout << "check\n"; + g.print(); + for(int t = 0; !g.checkWin(p); t++) { + tile ti = t%2 == 0 ? p1 : p2; + while(true){ + int y,x; std::cin >> y >> x; + pos pl(y,x); + if(!g.place(pl,ti)) { + p = pl; + break; + } + } + g.print(); + } + + return 0; +} diff --git a/recursive.cpp b/recursive.cpp new file mode 100644 index 0000000..3125793 --- /dev/null +++ b/recursive.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include +#include +#include +#include + +using std::deque; +using std::string; +using std::array; +using std::vector; +using std::min; +using std::cin; +using std::cout; + +enum tile {fr = 0, p1 = 1, p2 = 2, tie = 3}; +tile act(int t) { return (t % 2) ? p2 : p1; } +tile pas(int t) { return (t % 2) ? p1 : p2; } +#define point(pos) {(float)pos.x, (float)pos.y} + +#define FPS 15 +#define SCREEN 800 +#define LINE_W 2 + +#define IMG_COUNT 2 +array textures; +void prepareTextures() { + const char* paths[] = {"img/circle.png", "img/cross.png"}; + for(int i = 0; i < IMG_COUNT; i++) { + Image img; + img = LoadImage(paths[i]); + textures[i] = LoadTextureFromImage(img); + } +} +void renderImage(tile t, Rectangle dst, Color color = WHITE) { + if(t == fr || t == tie) return; + Texture tex = textures[t-1]; + Rectangle src = {0, 0, (float)tex.width, (float)tex.height}; + DrawTexturePro(textures[t-1], src, dst, {0,0}, 0, color); +} + +struct pos { + int x, y; + pos() : x(-1), y(-1) {} + pos(int px, int py) : x(px), y(py) {} + pos(Vector2 v) : x(v.x), y(v.y) {} + pos normalize() { + if(x >= 3) x = 2; + else if(x < 0) x = 0; + if(y >= 3) y = 2; + else if(y < 0) y = 0; + return *this; + } + pos operator/(int a) {return {x/a, y/a};}; + pos operator%(int a) {return {x%a, y%a};}; + int to_int() { return 3*y + x; } + deque to_deque(int depth, int screenSize) { + deque r = {}; + pos pp = pos(x,y); + int pwr = 1; + for(int i = 1; i <= depth; i++) { + pwr *= 3; + pos ppos = (pp/(screenSize/pwr)); + if(ppos.x > 2 || ppos.y > 2) return {}; + r.push_back(ppos.to_int()); + pp = pp%(screenSize/pwr); + } + return r; + } +}; + +#define OKAY -1 +#define CHANGE -2 +struct Block { + tile t = fr; + bool leaf = false; + Rectangle target; + array sons; + + Block() {} + Block(int depth, Rectangle trg) { + target = trg; + int x = trg.x, y = trg.y, w = trg.width, h = trg.height; + if(depth == 0) leaf = true; + else + for(int i = 0; i < 9; i++) + sons[i] = new Block(depth-1, + {float(int(x + w/3 * (i%3))), float(int(y + h/3 * (i/3))), float(int(w/3)), float(int(h/3))}); + } + int change(tile pl) { + t = pl; + if(pl == fr) + return OKAY; + return CHANGE; + } + bool playable(deque v) { + if(leaf && t == fr) return true; + if(t != fr || v.size() == 0) return false; + int son = v.front(); + v.pop_front(); + return sons[son]->playable(v); + } + int play(deque v, tile pl) { + if(t != fr) return v.size(); + if(leaf) return change(pl); + if(v.size() == 0) return OKAY; + + int son = v.front(); + v.erase(v.begin()); + int rv = sons[son]->play(v, pl); + + if(rv == CHANGE) return change(check_win()); + return rv; + } + tile check_win() { + tile d1 = sons[0]->t, d2 = sons[2]->t; + for(int k = 0; k < 3; k++) { + tile row = sons[k*3]->t, col = sons[k%3]->t; + for(int i = 0; i < 3; i++) { + if(sons[k*3 + i]->t != row) row = fr; + if(sons[k%3 + i*3]->t != col) col = fr; + } + if(row != fr) return row; + if(col != fr) return col; + if(sons[k*4]->t != sons[0]->t) d1 = fr; + if(sons[(k+1)*2]->t != sons[2]->t) d2 = fr; + } + if(d1 != fr) return d1; + if(d2 != fr) return d2; + + bool is_tie = true; + for(Block* s : sons) + if(s->t == fr) is_tie = false; + if(is_tie) return tie; + + return fr; + } + void render(int depth) { + if(!leaf) DrawRectangleLinesEx(target, depth, BLACK); + renderImage(t, target); + if(leaf || (t != fr && t != tie)) return; + + for(int i = 0; i < 9; i++) + sons[i]->render(depth-1); + } + Rectangle getRect(deque v) { + if(v.size() == 0) return target; + else { + int index = v.front(); + v.pop_front(); + return sons[index]->getRect(v); + } + } + void updateRect(Rectangle trg) { + target = trg; + int x = trg.x, y = trg.y, w = trg.width, h = trg.height; + if(leaf) return; + for(int i = 0; i < 9; i++) + sons[i]->updateRect({ + float(int(x + w/3 * (i%3))), + float(int(y + h/3 * (i/3))), + float(int(w/3)), + float(int(h/3)) + }); + } + + void save(vector *r) { + if(leaf) r->push_back(t); + else + for(Block* son : sons) + son->save(r); + } + void load_state(vector state, int pos) { + if(leaf) t = state[pos]; + else { + for(int i = 0; i < 9; i++) + sons[i]->load_state(state, pos*9+i); + t = check_win(); + } + } + void undo(deque &prev) { + // TODO + } +}; + +void save_state(Block* root, int depth, int time, deque &turn) { + vector state = {}; + root->save(&state); + string filename; std::cin >> filename; + std::ofstream outfile("saves/" + filename); + outfile << depth << "\n" << time << "\n" << turn.size() << "\n"; + for(int play : turn) + outfile << play << std::endl; + for(tile t : state) + outfile << int(t) << std::endl; +} +void load_state(Block** root, deque &turn, int &depth, int &time, char* argv) { + depth = atoi(argv); + if(depth) + *root = new Block(depth, {0, 0, SCREEN, SCREEN}); + else { + string path = "saves/" + string(argv); + std::ifstream infile(path); + infile >> depth >> time; + int K; infile >> K; + for(int k = 0; k < K; k++) { + int play; infile >> play; + turn.push_back(play); + cout << play; + } + vector state = {}; + long long pw = 1; + for(int i = 0; i < depth; i++) pw *= 9; + for(int i = 0; i < pw; i++) { + int next; infile >> next; + state.push_back((tile)next); + } + *root = new Block(depth, {0, 0, SCREEN, SCREEN}); + (*root)->load_state(state, 0); + } +} + +void update(int &time, deque &turn, Block* root, Rectangle &dst) { + time++; + turn.erase(turn.begin()); + int rval_2 = root->play(turn, pas(time)); + for(int i = 0; i <= rval_2; i++) + turn.pop_back(); + dst = root->getRect(turn); +} +void undo(Block *root, deque &prev, Rectangle &dst, int &time) { + if(prev.size() == 0) return; + time--; + root->undo(prev); + // TODO +} +bool valid(Block* root, Rectangle &dst, deque &turn) { + return CheckCollisionRecs(root->getRect(turn), dst) && root->playable(turn); +} +void render_all(Block* root, int time, Rectangle dst, int depth, deque highlight = {}) { + BeginDrawing(); + ClearBackground(RAYWHITE); + root->render(depth); + if(valid(root, dst, highlight)) + renderImage(act(time), root->getRect(highlight), BLACK); + DrawRectangleLinesEx(dst, 3*LINE_W, act(time+1) == p2 ? BLUE : RED); + EndDrawing(); +} +void prepareScreen(Block* root, int time, Rectangle dst, int depth) { + SetConfigFlags(FLAG_WINDOW_RESIZABLE); + InitWindow(SCREEN, SCREEN, "Rekurze"); + SetTargetFPS(FPS); + prepareTextures(); + render_all(root, time, dst, depth); +} + + +int main(int argc, char** argv) { + deque turn = {}; + int time = 0, depth; + Block* root = nullptr; + + int screenSize = SCREEN; + + if(argc <= 1) { + cin >> depth; + root = new Block(depth, {0, 0, SCREEN, SCREEN}); + } + else load_state(&root, turn, depth, time, argv[1]); + + Rectangle dst = root->getRect(turn); + + prepareScreen(root, time, dst, depth); + + + bool end = false; + deque pturn = {}; + while(!WindowShouldClose()) { + // Update screen size + screenSize = min(GetScreenWidth(), GetScreenHeight()); + root->updateRect({0, 0, (float)screenSize, (float)screenSize}); + dst = root->getRect(pturn); + + // Move + Vector2 touch = GetMousePosition(); + turn = pos(touch).to_deque(depth, screenSize); + if(IsMouseButtonPressed(0) && CheckCollisionPointRec(touch, dst) && valid(root, dst, turn)) { + int rval = root->play(turn, act(time)); + if(rval == CHANGE) end = true; + else if(rval == OKAY) { + update(time, turn, root, dst); + pturn = turn; + } + } + + // Key presses + if(IsKeyPressed(KEY_X)) save_state(root, depth, time, turn), end = true; + else if(IsKeyPressed(KEY_Z)); //TODO undo + else if(IsKeyPressed(KEY_P)) TakeScreenshot("screenshot.png"); + + // Rendering + render_all(root, time, dst, depth, turn); + + if(end) break; + } + + WaitTime(10); + CloseWindow(); + return 0; +}