#include #include #include #include #include #include #include using std::deque; using std::string; using std::array; using std::vector; using std::cin; using std::cout; enum tile {empty = 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; } Color col(int t) { return act(t) == p2 ? BLUE : RED; } #define FPS 15 #define SCREEN 800 #define LINE_W 2 // Images #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 == empty || 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 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); if(pp.x > screenSize || pp.y > screenSize) return {}; for(int i = 0; i < depth; ++i) { screenSize /= 3; pos ppos = pp/screenSize; r.push_back(ppos.to_int()); pp = pp%screenSize; } return r; } }; deque to_deque(Vector2 v, int depth, int size) { deque r = {}; int x = v.x, y = v.y; if(x >= size || y >= size || x < 0 || y < 0) return {}; for(int i = 0; i < depth; ++i) { size /= 3; r.push_back(3*(y/size) + x/size); x %= size; y %= size; } return r; } #define OKAY -1 #define CHANGE -2 class Block { tile t = empty; Rectangle place; int depth; array sons; public: Block() {} Block(int _depth, Rectangle trg) { place = trg; int x = trg.x, y = trg.y, w = trg.width, h = trg.height; depth = _depth; if(depth > 0) for(int i = 0; i < 9; i++) sons[i] = new Block( depth-1, get_son_rectangle(i) ); } Rectangle get_son_rectangle(int son) { return { float(int(place.x + place.width/3 * (son%3))), float(int(place.y + place.height/3 * int(son/3))), float(int(place.width/3)), float(int(place.height/3)) }; } int change(tile pl) { t = pl; if(pl == empty) return OKAY; return CHANGE; } bool playable(deque v) { if(depth == 0 && t == empty) return true; if(t != empty || 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 != empty) return v.size(); if(depth == 0) 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; } bool check_tie() { bool is_tie = true; for(Block* s : sons) if(s->t == empty) is_tie = false; return is_tie; } 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 = empty; if(sons[k%3 + i*3]->t != col) col = empty; } if(row != empty) return row; if(col != empty) return col; if(sons[k*4]->t != sons[0]->t) d1 = empty; if(sons[(k+1)*2]->t != sons[2]->t) d2 = empty; } if(d1 != empty) return d1; if(d2 != empty) return d2; if(check_tie()) return tie; return empty; } void render() { if(depth > 0) DrawRectangleLinesEx(place, depth, BLACK); renderImage(t, place); if(depth == 0 || (t != empty && t != tie)) return; for(int i = 0; i < 9; i++) sons[i]->render(); } Rectangle getRect(deque v) { if(v.size() == 0) return place; else { int index = v.front(); v.pop_front(); return sons[index]->getRect(v); } } void updateRect(Rectangle trg) { place = trg; int x = trg.x, y = trg.y, w = trg.width, h = trg.height; if(depth == 0) return; for(int i = 0; i < 9; i++) sons[i]->updateRect(get_son_rectangle(i)); } void save(vector *r) { if(depth == 0) r->push_back(t); else // TODO save? for(Block* son : sons) son->save(r); } void load_state(vector state, int pos) { if(depth == 0) 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) { // TODall: $(PAGES) ...O } }; 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, deque highlight = {}) { BeginDrawing(); ClearBackground(RAYWHITE); root->render(); if(valid(root, dst, highlight)) renderImage(act(time), root->getRect(highlight), BLACK); DrawRectangleLinesEx(dst, 3*LINE_W, col(time+1)); 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); } 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, (float)screenSize, (float)screenSize}); } 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 = std::min(GetScreenWidth(), GetScreenHeight()); int size = 1; while(size <= screenSize) size *= 3; screenSize = size/3; float start_x = ((float)GetScreenWidth()-screenSize)/2; float start_y = ((float)GetScreenHeight()-screenSize)/2; root->updateRect({start_x, start_y, (float)screenSize, (float)screenSize}); dst = root->getRect(pturn); // Move Vector2 touch = GetMousePosition(); turn = to_deque({touch.x - start_x, touch.y - start_y}, depth, screenSize); if(IsMouseButtonPressed(0) && CheckCollisionPointRec(touch, dst) && valid(root, dst, turn)) { switch(root->play(turn, act(time))) { case CHANGE: end = true; break; case OKAY: update(time, turn, root, dst); pturn = turn; break; } } // Key presses if(IsKeyPressed(KEY_X)) save_state(root, depth, time, turn), end = true; else if(IsKeyPressed(KEY_Z)) {} //TODO undo // Rendering render_all(root, time, dst, turn); if(end) break; } WaitTime(10); CloseWindow(); return 0; }